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