xref: /DADK/dadk-user/src/parser/mod.rs (revision 5129c63bfab60a54627def29dc1b53c722677e4e)
1 //! # 配置解析器
2 //!
3 //! 用于解析配置文件,生成任务列表
4 //!
5 //! 您需要指定一个配置文件目录,解析器会自动解析该目录下的所有配置文件。
6 //! 软件包的配置文件格式为toml
7 //!
8 //! ## 简介
9 //!
10 //! 在每个配置文件中,您需要指定软件包的名称、版本、描述、任务类型、依赖、构建配置和安装配置。DADK会根据这些信息生成任务列表。
11 //!
12 //! ## 配置文件格式
13 //!
14 //! ```toml
15 //! name = "test_git"
16 //! version = "0.1.0"
17 //! description = ""
18 //! build_once = true
19 //! install_once = true
20 //! target_arch = ["x86_64"]
21 //!
22 //! [task_type]
23 //! type = "build_from_source"
24 //! source = "git"
25 //! source_path = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/test_git.git"
26 //! revison = "01cdc56863"
27 //! branch = "test"
28 //!
29 //! [build]
30 //! build-command = "make instal"
31 //!
32 //! [install]
33 //! in_dragonos_path = "/bin"
34 //!
35 //! [clean]
36 //! clean-command = "make clean"
37 //!
38 //! [depends]
39 //! depend1 = "0.1.1"
40 //! depend2 = "0.1.2"
41 //!
42 //! [envs]
43 //! PATH = "/usr/bin"
44 //! LD_LIBRARY_PATH = "/usr/lib"
45 
46 use std::{
47     fmt::Debug,
48     fs::{DirEntry, ReadDir},
49     path::PathBuf,
50 };
51 
52 use self::task::DADKTask;
53 use config::DADKUserConfig;
54 use log::{debug, error, info};
55 use task::{BuildConfig, CleanConfig, InstallConfig, TaskType};
56 use toml::Table;
57 mod config;
58 pub mod task;
59 pub mod task_log;
60 
61 /// # 配置解析器
62 ///
63 /// 用于解析配置文件,生成任务列表
64 #[derive(Debug)]
65 pub struct Parser {
66     /// 配置文件目录
67     config_dir: PathBuf,
68     /// 扫描到的配置文件列表
69     config_files: Vec<PathBuf>,
70 }
71 
72 pub struct ParserError {
73     pub config_file: Option<PathBuf>,
74     pub error: InnerParserError,
75 }
76 impl Debug for ParserError {
77     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78         match &self.error {
79             InnerParserError::IoError(e) => {
80                 if let Some(config_file) = &self.config_file {
81                     write!(
82                         f,
83                         "IO Error while parsing config file {}: {}",
84                         config_file.display(),
85                         e
86                     )
87                 } else {
88                     write!(f, "IO Error while parsing config files: {}", e)
89                 }
90             }
91             InnerParserError::TomlError(e) => {
92                 if let Some(config_file) = &self.config_file {
93                     write!(
94                         f,
95                         "Toml Error while parsing config file {}: {}",
96                         config_file.display(),
97                         e
98                     )
99                 } else {
100                     write!(f, "Toml Error while parsing config file: {}", e)
101                 }
102             }
103             InnerParserError::TaskError(e) => {
104                 if let Some(config_file) = &self.config_file {
105                     write!(
106                         f,
107                         "Error while parsing config file {}: {}",
108                         config_file.display(),
109                         e
110                     )
111                 } else {
112                     write!(f, "Error while parsing config file: {}", e)
113                 }
114             }
115         }
116     }
117 }
118 
119 #[derive(Debug)]
120 pub enum InnerParserError {
121     IoError(std::io::Error),
122     TomlError(toml::de::Error),
123     TaskError(String),
124 }
125 
126 impl Parser {
127     pub fn new(config_dir: PathBuf) -> Self {
128         Self {
129             config_dir,
130             config_files: Vec::new(),
131         }
132     }
133 
134     /// # 解析所有配置文件,生成任务列表
135     ///
136     /// ## 参数
137     ///
138     /// * `config_dir` - 配置文件所在目录
139     ///
140     /// ## 返回值
141     ///
142     /// * `Ok(Vec<(PathBuf, DADKTask)>)` - 任务列表(配置文件路径, 任务)
143     /// * `Err(ParserError)` - 解析错误
144     pub fn parse(&mut self) -> Result<Vec<(PathBuf, DADKTask)>, ParserError> {
145         self.scan_config_files()?;
146         info!("Found {} config files", self.config_files.len());
147         let r: Result<Vec<(PathBuf, DADKTask)>, ParserError> = self.gen_tasks();
148         if r.is_err() {
149             error!("Error while parsing config files: {:?}", r);
150         }
151         return r;
152     }
153 
154     /// # 扫描配置文件目录,找到所有配置文件
155     fn scan_config_files(&mut self) -> Result<(), ParserError> {
156         info!("Scanning config files in {}", self.config_dir.display());
157 
158         let mut dir_queue: Vec<PathBuf> = Vec::new();
159         // 将config目录加入队列
160         dir_queue.push(self.config_dir.clone());
161 
162         while !dir_queue.is_empty() {
163             // 扫描目录,找到所有*.dadk文件
164             let dir = dir_queue.pop().unwrap();
165             let entries: ReadDir = std::fs::read_dir(&dir).map_err(|e| ParserError {
166                 config_file: None,
167                 error: InnerParserError::IoError(e),
168             })?;
169 
170             for entry in entries {
171                 let entry: DirEntry = entry.map_err(|e| ParserError {
172                     config_file: None,
173                     error: InnerParserError::IoError(e),
174                 })?;
175 
176                 let path: PathBuf = entry.path();
177                 if path.is_dir() {
178                     dir_queue.push(path);
179                 } else if path.is_file() {
180                     let extension: Option<&std::ffi::OsStr> = path.extension();
181                     if extension.is_none() {
182                         continue;
183                     }
184                     let extension: &std::ffi::OsStr = extension.unwrap();
185                     if extension.to_ascii_lowercase() != "dadk" {
186                         continue;
187                     }
188                     // 找到一个配置文件, 加入列表
189                     self.config_files.push(path);
190                 }
191             }
192         }
193 
194         return Ok(());
195     }
196 
197     /// # 解析所有配置文件,生成任务列表
198     ///
199     /// 一旦发生错误,立即返回
200     ///
201     /// ## 返回值
202     ///
203     /// * `Ok(Vec<DADKTask>)` - 任务列表
204     /// * `Err(ParserError)` - 解析错误
205     fn gen_tasks(&self) -> Result<Vec<(PathBuf, DADKTask)>, ParserError> {
206         let mut result_vec = Vec::new();
207         for config_file in &self.config_files {
208             let task: DADKTask = self.parse_config_file(config_file)?;
209             debug!("Parsed config file {}: {:?}", config_file.display(), task);
210             result_vec.push((config_file.clone(), task));
211         }
212 
213         return Ok(result_vec);
214     }
215 
216     /// # 解析单个配置文件,生成任务
217     ///
218     /// ## 参数
219     ///
220     /// * `config_file` - 配置文件路径
221     ///
222     /// ## 返回值
223     ///
224     /// * `Ok(DADKTask)` - 生成好的任务
225     /// * `Err(ParserError)` - 解析错误
226     pub(super) fn parse_config_file(&self, config_file: &PathBuf) -> Result<DADKTask, ParserError> {
227         // 从toml文件中解析出DADKTask
228         let mut task: DADKTask = Self::parse_toml_file(config_file)?;
229 
230         debug!("Parsed config file {}: {:?}", config_file.display(), task);
231 
232         // 去除字符串中的空白字符
233         task.trim();
234 
235         // 校验DADKTask的参数是否合法
236         task.validate().map_err(|e| ParserError {
237             config_file: Some(config_file.clone()),
238             error: InnerParserError::TaskError(e),
239         })?;
240 
241         return Ok(task);
242     }
243 
244     /// 解析toml文件,生成DADKTask
245     pub fn parse_toml_file(config_file: &PathBuf) -> Result<DADKTask, ParserError> {
246         let content = std::fs::read_to_string(config_file).map_err(|e| ParserError {
247             config_file: Some(config_file.clone()),
248             error: InnerParserError::IoError(e),
249         })?;
250 
251         let table = content.parse::<Table>().map_err(|e| ParserError {
252             config_file: Some(config_file.clone()),
253             error: InnerParserError::TomlError(e),
254         })?;
255 
256         let dadk_user_config = DADKUserConfig::parse(config_file, &table)?;
257 
258         Ok(DADKTask {
259             name: dadk_user_config.standard_config.name,
260             version: dadk_user_config.standard_config.version,
261             description: dadk_user_config.standard_config.description,
262             rust_target: dadk_user_config.standard_config.rust_target,
263             task_type: TaskType::try_from(dadk_user_config.task_type_config)?,
264             depends: dadk_user_config.depends_config.depends,
265             build: BuildConfig::from(dadk_user_config.build_config),
266             install: InstallConfig::from(dadk_user_config.install_config),
267             clean: CleanConfig::from(dadk_user_config.clean_config),
268             envs: dadk_user_config.envs_config.envs,
269             build_once: dadk_user_config.standard_config.build_once,
270             install_once: dadk_user_config.standard_config.install_once,
271             target_arch: dadk_user_config.standard_config.target_arch,
272         })
273     }
274 }
275