xref: /DADK/dadk-user/src/parser/mod.rs (revision 73779f3d0abacaf05aae9b67820e68f4bb9cf53f)
1 //! # 配置解析器
2 //!
3 //! 用于解析配置文件,生成任务列表
4 //!
5 //! 您需要指定一个配置文件目录,解析器会自动解析该目录下的所有配置文件。
6 //! 软件包的配置文件必须以`.dadk`作为后缀名,内容格式为json。
7 //!
8 //! ## 简介
9 //!
10 //! 在每个配置文件中,您需要指定软件包的名称、版本、描述、任务类型、依赖、构建配置和安装配置。DADK会根据这些信息生成任务列表。
11 //!
12 //! ## 配置文件格式
13 //!
14 //! ```json
15 //! {
16 //!     "name": "软件包名称",
17 //!     "version": "软件包版本",
18 //!     "description": "软件包描述",
19 //!     "task_type": {任务类型(该部分详见`TaskType`的文档)},
20 //!     "depends": [{依赖项(该部分详见Dependency的文档)}],
21 //!     "build": {构建配置(该部分详见BuildConfig的文档)},
22 //!     "install": {安装配置(该部分详见InstallConfig的文档)},
23 //!     "envs" : [{ "key": "环境变量名", "value": "环境变量值" }]
24 //!     "build_once": (可选) 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果。
25 //! }
26 use std::{
27     fmt::Debug,
28     fs::{DirEntry, ReadDir},
29     path::PathBuf,
30 };
31 
32 use log::{debug, error, info};
33 
34 use self::task::DADKTask;
35 pub mod task;
36 pub mod task_log;
37 #[cfg(test)]
38 mod tests;
39 
40 /// # 配置解析器
41 ///
42 /// 用于解析配置文件,生成任务列表
43 #[derive(Debug)]
44 pub struct Parser {
45     /// 配置文件目录
46     config_dir: PathBuf,
47     /// 扫描到的配置文件列表
48     config_files: Vec<PathBuf>,
49 }
50 
51 pub struct ParserError {
52     pub config_file: Option<PathBuf>,
53     pub error: InnerParserError,
54 }
55 impl Debug for ParserError {
56     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57         match &self.error {
58             InnerParserError::IoError(e) => {
59                 if let Some(config_file) = &self.config_file {
60                     write!(
61                         f,
62                         "IO Error while parsing config file {}: {}",
63                         config_file.display(),
64                         e
65                     )
66                 } else {
67                     write!(f, "IO Error while parsing config files: {}", e)
68                 }
69             }
70             InnerParserError::JsonError(e) => {
71                 if let Some(config_file) = &self.config_file {
72                     write!(
73                         f,
74                         "Json Error while parsing config file {}: {}",
75                         config_file.display(),
76                         e
77                     )
78                 } else {
79                     write!(f, "Json Error while parsing config file: {}", e)
80                 }
81             }
82             InnerParserError::TaskError(e) => {
83                 if let Some(config_file) = &self.config_file {
84                     write!(
85                         f,
86                         "Error while parsing config file {}: {}",
87                         config_file.display(),
88                         e
89                     )
90                 } else {
91                     write!(f, "Error while parsing config file: {}", e)
92                 }
93             }
94         }
95     }
96 }
97 
98 #[derive(Debug)]
99 pub enum InnerParserError {
100     IoError(std::io::Error),
101     JsonError(serde_json::Error),
102     TaskError(String),
103 }
104 
105 impl Parser {
106     pub fn new(config_dir: PathBuf) -> Self {
107         Self {
108             config_dir,
109             config_files: Vec::new(),
110         }
111     }
112 
113     /// # 解析所有配置文件,生成任务列表
114     ///
115     /// ## 参数
116     ///
117     /// * `config_dir` - 配置文件所在目录
118     ///
119     /// ## 返回值
120     ///
121     /// * `Ok(Vec<(PathBuf, DADKTask)>)` - 任务列表(配置文件路径, 任务)
122     /// * `Err(ParserError)` - 解析错误
123     pub fn parse(&mut self) -> Result<Vec<(PathBuf, DADKTask)>, ParserError> {
124         self.scan_config_files()?;
125         info!("Found {} config files", self.config_files.len());
126         let r: Result<Vec<(PathBuf, DADKTask)>, ParserError> = self.gen_tasks();
127         if r.is_err() {
128             error!("Error while parsing config files: {:?}", r);
129         }
130         return r;
131     }
132 
133     /// # 扫描配置文件目录,找到所有配置文件
134     fn scan_config_files(&mut self) -> Result<(), ParserError> {
135         info!("Scanning config files in {}", self.config_dir.display());
136 
137         let mut dir_queue: Vec<PathBuf> = Vec::new();
138         // 将config目录加入队列
139         dir_queue.push(self.config_dir.clone());
140 
141         while !dir_queue.is_empty() {
142             // 扫描目录,找到所有*.dadk文件
143             let dir = dir_queue.pop().unwrap();
144             let entries: ReadDir = std::fs::read_dir(&dir).map_err(|e| ParserError {
145                 config_file: None,
146                 error: InnerParserError::IoError(e),
147             })?;
148 
149             for entry in entries {
150                 let entry: DirEntry = entry.map_err(|e| ParserError {
151                     config_file: None,
152                     error: InnerParserError::IoError(e),
153                 })?;
154 
155                 let path: PathBuf = entry.path();
156                 if path.is_dir() {
157                     dir_queue.push(path);
158                 } else if path.is_file() {
159                     let extension: Option<&std::ffi::OsStr> = path.extension();
160                     if extension.is_none() {
161                         continue;
162                     }
163                     let extension: &std::ffi::OsStr = extension.unwrap();
164                     if extension.to_ascii_lowercase() != "dadk" {
165                         continue;
166                     }
167                     // 找到一个配置文件, 加入列表
168                     self.config_files.push(path);
169                 }
170             }
171         }
172 
173         return Ok(());
174     }
175 
176     /// # 解析所有配置文件,生成任务列表
177     ///
178     /// 一旦发生错误,立即返回
179     ///
180     /// ## 返回值
181     ///
182     /// * `Ok(Vec<DADKTask>)` - 任务列表
183     /// * `Err(ParserError)` - 解析错误
184     fn gen_tasks(&self) -> Result<Vec<(PathBuf, DADKTask)>, ParserError> {
185         let mut result_vec = Vec::new();
186         for config_file in &self.config_files {
187             let task: DADKTask = self.parse_config_file(config_file)?;
188             debug!("Parsed config file {}: {:?}", config_file.display(), task);
189             result_vec.push((config_file.clone(), task));
190         }
191 
192         return Ok(result_vec);
193     }
194 
195     /// # 解析单个配置文件,生成任务
196     ///
197     /// ## 参数
198     ///
199     /// * `config_file` - 配置文件路径
200     ///
201     /// ## 返回值
202     ///
203     /// * `Ok(DADKTask)` - 生成好的任务
204     /// * `Err(ParserError)` - 解析错误
205     pub(super) fn parse_config_file(&self, config_file: &PathBuf) -> Result<DADKTask, ParserError> {
206         let content = std::fs::read_to_string(config_file).map_err(|e| ParserError {
207             config_file: Some(config_file.clone()),
208             error: InnerParserError::IoError(e),
209         })?;
210 
211         // 从json字符串中解析出DADKTask
212         let mut task: DADKTask = serde_json::from_str(&content).map_err(|e| ParserError {
213             config_file: Some(config_file.clone()),
214             error: InnerParserError::JsonError(e),
215         })?;
216 
217         debug!("Parsed config file {}: {:?}", config_file.display(), task);
218 
219         // 去除字符串中的空白字符
220         task.trim();
221 
222         // 校验DADKTask的参数是否合法
223         task.validate().map_err(|e| ParserError {
224             config_file: Some(config_file.clone()),
225             error: InnerParserError::TaskError(e),
226         })?;
227 
228         return Ok(task);
229     }
230 }
231