xref: /DADK/dadk-user/src/parser/mod.rs (revision e0934047cd911bf12b893ec79fa0f5c8ee6402b9)
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 anyhow::Result;
54 use dadk_config::user::UserConfigFile;
55 use log::{debug, error, info};
56 
57 pub mod task;
58 pub mod task_log;
59 
60 /// # 配置解析器
61 ///
62 /// 用于解析配置文件,生成任务列表
63 #[derive(Debug)]
64 pub struct Parser {
65     /// 配置文件目录
66     config_dir: PathBuf,
67     /// 扫描到的配置文件列表
68     config_files: Vec<PathBuf>,
69 }
70 
71 pub struct ParserError {
72     pub config_file: Option<PathBuf>,
73     pub error: InnerParserError,
74 }
75 impl Debug for ParserError {
76     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77         match &self.error {
78             InnerParserError::IoError(e) => {
79                 if let Some(config_file) = &self.config_file {
80                     write!(
81                         f,
82                         "IO Error while parsing config file {}: {}",
83                         config_file.display(),
84                         e
85                     )
86                 } else {
87                     write!(f, "IO Error while parsing config files: {}", e)
88                 }
89             }
90             InnerParserError::TomlError(e) => {
91                 if let Some(config_file) = &self.config_file {
92                     write!(
93                         f,
94                         "Toml Error while parsing config file {}: {}",
95                         config_file.display(),
96                         e
97                     )
98                 } else {
99                     write!(f, "Toml Error while parsing config file: {}", e)
100                 }
101             }
102             InnerParserError::TaskError(e) => {
103                 if let Some(config_file) = &self.config_file {
104                     write!(
105                         f,
106                         "Error while parsing config file {}: {}",
107                         config_file.display(),
108                         e
109                     )
110                 } else {
111                     write!(f, "Error while parsing config file: {}", e)
112                 }
113             }
114         }
115     }
116 }
117 
118 #[derive(Debug)]
119 pub enum InnerParserError {
120     IoError(std::io::Error),
121     TomlError(toml::de::Error),
122     TaskError(String),
123 }
124 
125 impl Parser {
126     pub fn new(config_dir: PathBuf) -> Self {
127         Self {
128             config_dir,
129             config_files: Vec::new(),
130         }
131     }
132 
133     /// # 解析所有配置文件,生成任务列表
134     ///
135     /// ## 参数
136     ///
137     /// * `config_dir` - 配置文件所在目录
138     ///
139     /// ## 返回值
140     ///
141     /// * `Ok(Vec<(PathBuf, DADKTask)>)` - 任务列表(配置文件路径, 任务)
142     /// * `Err(ParserError)` - 解析错误
143     pub fn parse(&mut self) -> Result<Vec<(PathBuf, DADKTask)>> {
144         self.scan_config_files()?;
145         info!("Found {} config files", self.config_files.len());
146         let r: Result<Vec<(PathBuf, DADKTask)>> = self.gen_tasks();
147         if r.is_err() {
148             error!("Error while parsing config files: {:?}", r);
149         }
150         return r;
151     }
152 
153     /// # 扫描配置文件目录,找到所有配置文件
154     fn scan_config_files(&mut self) -> Result<()> {
155         info!("Scanning config files in {}", self.config_dir.display());
156 
157         let mut dir_queue: Vec<PathBuf> = Vec::new();
158         // 将config目录加入队列
159         dir_queue.push(self.config_dir.clone());
160 
161         while !dir_queue.is_empty() {
162             // 扫描目录,找到所有*.dadk文件
163             let dir = dir_queue.pop().unwrap();
164             let entries: ReadDir = std::fs::read_dir(&dir)?;
165 
166             for entry in entries {
167                 let entry: DirEntry = entry?;
168 
169                 let path: PathBuf = entry.path();
170                 if path.is_dir() {
171                     dir_queue.push(path);
172                 } else if path.is_file() {
173                     let extension: Option<&std::ffi::OsStr> = path.extension();
174                     if extension.is_none() {
175                         continue;
176                     }
177                     let extension: &std::ffi::OsStr = extension.unwrap();
178                     if extension.to_ascii_lowercase() != "toml" {
179                         continue;
180                     }
181                     // 找到一个配置文件, 加入列表
182                     self.config_files.push(path);
183                 }
184             }
185         }
186 
187         return Ok(());
188     }
189 
190     /// # 解析所有配置文件,生成任务列表
191     ///
192     /// 一旦发生错误,立即返回
193     ///
194     /// ## 返回值
195     ///
196     /// * `Ok(Vec<DADKTask>)` - 任务列表
197     /// * `Err(ParserError)` - 解析错误
198     fn gen_tasks(&self) -> Result<Vec<(PathBuf, DADKTask)>> {
199         let mut result_vec = Vec::new();
200         for config_file in &self.config_files {
201             let task: DADKTask = self.parse_config_file(config_file)?;
202             debug!("Parsed config file {}: {:?}", config_file.display(), task);
203             result_vec.push((config_file.clone(), task));
204         }
205 
206         return Ok(result_vec);
207     }
208 
209     /// # 解析单个配置文件,生成任务
210     ///
211     /// ## 参数
212     ///
213     /// * `config_file` - 配置文件路径
214     ///
215     /// ## 返回值
216     ///
217     /// * `Ok(DADKTask)` - 生成好的任务
218     /// * `Err(ParserError)` - 解析错误
219     pub(super) fn parse_config_file(&self, config_file: &PathBuf) -> Result<DADKTask> {
220         log::trace!("Parsing config file {}", config_file.display());
221         // 从toml文件中解析出DADKTask
222         let mut task: DADKTask = Self::parse_toml_file(config_file)?;
223 
224         // 去除字符串中的空白字符
225         task.trim();
226 
227         // 校验DADKTask的参数是否合法
228         task.validate()?;
229 
230         return Ok(task);
231     }
232 
233     /// 解析toml文件,生成DADKTask
234     pub fn parse_toml_file(config_file: &PathBuf) -> Result<DADKTask> {
235         let dadk_user_config = UserConfigFile::load(config_file)?;
236         DADKTask::try_from(dadk_user_config)
237     }
238 }
239