dadk_user/parser/
mod.rs

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
46use std::{
47    fmt::Debug,
48    fs::{DirEntry, ReadDir},
49    path::PathBuf,
50};
51
52use self::task::DADKTask;
53use anyhow::Result;
54use dadk_config::user::UserConfigFile;
55use log::{debug, error, info};
56
57pub mod task;
58pub mod task_log;
59
60/// # 配置解析器
61///
62/// 用于解析配置文件,生成任务列表
63#[derive(Debug)]
64pub struct Parser {
65    /// 配置文件目录
66    config_dir: PathBuf,
67    /// 扫描到的配置文件列表
68    config_files: Vec<PathBuf>,
69}
70
71pub struct ParserError {
72    pub config_file: Option<PathBuf>,
73    pub error: InnerParserError,
74}
75impl 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)]
119pub enum InnerParserError {
120    IoError(std::io::Error),
121    TomlError(toml::de::Error),
122    TaskError(String),
123}
124
125impl 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}