//! # 配置解析器 //! //! 用于解析配置文件,生成任务列表 //! //! 您需要指定一个配置文件目录,解析器会自动解析该目录下的所有配置文件。 //! 软件包的配置文件格式为toml //! //! ## 简介 //! //! 在每个配置文件中,您需要指定软件包的名称、版本、描述、任务类型、依赖、构建配置和安装配置。DADK会根据这些信息生成任务列表。 //! //! ## 配置文件格式 //! //! ```toml //! name = "test_git" //! version = "0.1.0" //! description = "" //! build_once = true //! install_once = true //! target_arch = ["x86_64"] //! //! [task_type] //! type = "build_from_source" //! source = "git" //! source_path = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/test_git.git" //! revison = "01cdc56863" //! branch = "test" //! //! [build] //! build-command = "make instal" //! //! [install] //! in_dragonos_path = "/bin" //! //! [clean] //! clean-command = "make clean" //! //! [depends] //! depend1 = "0.1.1" //! depend2 = "0.1.2" //! //! [envs] //! PATH = "/usr/bin" //! LD_LIBRARY_PATH = "/usr/lib" use std::{ fmt::Debug, fs::{DirEntry, ReadDir}, path::PathBuf, }; use self::task::DADKTask; use anyhow::Result; use dadk_config::user::UserConfigFile; use log::{debug, error, info}; pub mod task; pub mod task_log; /// # 配置解析器 /// /// 用于解析配置文件,生成任务列表 #[derive(Debug)] pub struct Parser { /// 配置文件目录 config_dir: PathBuf, /// 扫描到的配置文件列表 config_files: Vec, } pub struct ParserError { pub config_file: Option, pub error: InnerParserError, } impl Debug for ParserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.error { InnerParserError::IoError(e) => { if let Some(config_file) = &self.config_file { write!( f, "IO Error while parsing config file {}: {}", config_file.display(), e ) } else { write!(f, "IO Error while parsing config files: {}", e) } } InnerParserError::TomlError(e) => { if let Some(config_file) = &self.config_file { write!( f, "Toml Error while parsing config file {}: {}", config_file.display(), e ) } else { write!(f, "Toml Error while parsing config file: {}", e) } } InnerParserError::TaskError(e) => { if let Some(config_file) = &self.config_file { write!( f, "Error while parsing config file {}: {}", config_file.display(), e ) } else { write!(f, "Error while parsing config file: {}", e) } } } } } #[derive(Debug)] pub enum InnerParserError { IoError(std::io::Error), TomlError(toml::de::Error), TaskError(String), } impl Parser { pub fn new(config_dir: PathBuf) -> Self { Self { config_dir, config_files: Vec::new(), } } /// # 解析所有配置文件,生成任务列表 /// /// ## 参数 /// /// * `config_dir` - 配置文件所在目录 /// /// ## 返回值 /// /// * `Ok(Vec<(PathBuf, DADKTask)>)` - 任务列表(配置文件路径, 任务) /// * `Err(ParserError)` - 解析错误 pub fn parse(&mut self) -> Result> { self.scan_config_files()?; info!("Found {} config files", self.config_files.len()); let r: Result> = self.gen_tasks(); if r.is_err() { error!("Error while parsing config files: {:?}", r); } return r; } /// # 扫描配置文件目录,找到所有配置文件 fn scan_config_files(&mut self) -> Result<()> { info!("Scanning config files in {}", self.config_dir.display()); let mut dir_queue: Vec = Vec::new(); // 将config目录加入队列 dir_queue.push(self.config_dir.clone()); while !dir_queue.is_empty() { // 扫描目录,找到所有*.dadk文件 let dir = dir_queue.pop().unwrap(); let entries: ReadDir = std::fs::read_dir(&dir)?; for entry in entries { let entry: DirEntry = entry?; let path: PathBuf = entry.path(); if path.is_dir() { dir_queue.push(path); } else if path.is_file() { let extension: Option<&std::ffi::OsStr> = path.extension(); if extension.is_none() { continue; } let extension: &std::ffi::OsStr = extension.unwrap(); if extension.to_ascii_lowercase() != "toml" { continue; } // 找到一个配置文件, 加入列表 self.config_files.push(path); } } } return Ok(()); } /// # 解析所有配置文件,生成任务列表 /// /// 一旦发生错误,立即返回 /// /// ## 返回值 /// /// * `Ok(Vec)` - 任务列表 /// * `Err(ParserError)` - 解析错误 fn gen_tasks(&self) -> Result> { let mut result_vec = Vec::new(); for config_file in &self.config_files { let task: DADKTask = self.parse_config_file(config_file)?; debug!("Parsed config file {}: {:?}", config_file.display(), task); result_vec.push((config_file.clone(), task)); } return Ok(result_vec); } /// # 解析单个配置文件,生成任务 /// /// ## 参数 /// /// * `config_file` - 配置文件路径 /// /// ## 返回值 /// /// * `Ok(DADKTask)` - 生成好的任务 /// * `Err(ParserError)` - 解析错误 pub(super) fn parse_config_file(&self, config_file: &PathBuf) -> Result { log::trace!("Parsing config file {}", config_file.display()); // 从toml文件中解析出DADKTask let mut task: DADKTask = Self::parse_toml_file(config_file)?; // 去除字符串中的空白字符 task.trim(); // 校验DADKTask的参数是否合法 task.validate()?; return Ok(task); } /// 解析toml文件,生成DADKTask pub fn parse_toml_file(config_file: &PathBuf) -> Result { let dadk_user_config = UserConfigFile::load(config_file)?; DADKTask::try_from(dadk_user_config) } }