diff --git a/examples/full-spec/rask.yaml b/examples/full-spec/rask.yaml index 9199c75..325268e 100644 --- a/examples/full-spec/rask.yaml +++ b/examples/full-spec/rask.yaml @@ -6,7 +6,6 @@ directories: - hello - project-* - nested - - duplicate-* tasks: dev: echo 'Hello from main!' diff --git a/src/commands/run.rs b/src/commands/run.rs index 27cee48..78139af 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -1,5 +1,5 @@ use clap::Args; -use crate::utils::config_reader::parse_config; +use crate::utils::config::{parse_config, validate_config}; use crate::utils::file_resolvers::resolve_configuration_file; #[derive(Args, Debug)] @@ -17,6 +17,11 @@ pub fn run (arguments: &Arguments) -> Result<(), String> { let config = parse_config(&target)?; + match validate_config(config.clone()) { + Ok(_) => {} + Err(err) => return Err(err) + } + println!("{:?}", config); Ok(()) diff --git a/src/utils/config.rs b/src/utils/config.rs new file mode 100644 index 0000000..bd27236 --- /dev/null +++ b/src/utils/config.rs @@ -0,0 +1,118 @@ +use std::path::PathBuf; +use std::fs::File; +use std::io::BufReader; +use serde::Deserialize; +use std::collections::HashMap; + + +pub fn validate_config(config: Config) -> Result { + let mut names: Vec = vec![]; + + for config in config.iter() { + let Config { name, path, .. } = config; + + if names.contains(&name) { + return Err(format!("Duplicate config name {} found: {:?}", name, path)); + } + + names.push(name); + } + + Ok(true) +} + +#[derive(Debug, Deserialize, Default)] +struct ConfigFile { + name: String, + #[serde(default)] + directories: Vec, + #[serde(default)] + tasks: HashMap, +} + +fn read_config_file(path_buf: &PathBuf) -> Result { + let file = File::open(path_buf).expect("Failed to read file"); + + let reader = BufReader::new(file); + + let config: ConfigFile = serde_yaml::from_reader(reader).expect("Failed to parse YAML"); + + Ok(config) +} + +#[derive(Debug, Clone)] +pub struct ConfigTask { + tag: String, + command: String +} + +#[derive(Debug, Clone)] +pub struct Config { + name: String, + tasks: Vec, + path: PathBuf, + sub_configs: Vec, +} + +pub struct ConfigIterator { + stack: Vec +} + +impl ConfigIterator { + pub fn new(config: Config) -> ConfigIterator { + let mut stack = vec![config]; + ConfigIterator{ stack } + } +} + +impl Iterator for ConfigIterator { + type Item = Config; + + fn next(&mut self) -> Option { + let next_config = self.stack.pop()?; + + self.stack.extend(next_config.sub_configs.iter().rev().map(|sub_config| sub_config.clone())); + + Some(next_config) + } +} + +impl Config { + fn iter(self) -> ConfigIterator { + return ConfigIterator::new(self.clone()); + } +} + +pub fn parse_config(path: &PathBuf) -> Result { + let config_file = read_config_file(path)?; + + let name = config_file.name; + + let tasks = config_file.tasks + .iter() + .map(|(tag, command)| ConfigTask{tag: tag.clone(), command: command.clone()}) + .collect(); + + let mut sub_configs: Vec = Vec::new(); + + let parent_dir = path.parent().ok_or("Failed to get parent directory")?; + + for directory in config_file.directories { + let mut pattern: PathBuf = parent_dir.to_path_buf(); + pattern.push(&directory); + if !pattern.ends_with(".yaml") { + pattern.push("rask.yaml"); + } + + for entry in glob::glob(pattern.to_str().unwrap()).map_err(|e| format!("Failed to read glob pattern: {}", e))? { + if let Ok(config_path) = entry { + let sub_config = parse_config(&config_path)?; + sub_configs.push(sub_config); + } + } + } + + let config = Config{name, tasks, sub_configs, path: path.clone()}; + + Ok(config) +} diff --git a/src/utils/config_reader.rs b/src/utils/config_reader.rs deleted file mode 100644 index 4f7d3aa..0000000 --- a/src/utils/config_reader.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::collections::HashMap; -use serde::Deserialize; -use std::fs::File; -use std::io::BufReader; -use std::path::PathBuf; -use glob::glob; - -#[derive(Debug, Deserialize, Default)] -struct ConfigFile { - name: String, - #[serde(default)] - directories: Vec, - #[serde(default)] - tasks: HashMap, -} - -fn read_config_file(path_buf: &PathBuf) -> Result { - let file = File::open(path_buf).expect("Failed to read file"); - - let reader = BufReader::new(file); - - let config: ConfigFile = serde_yaml::from_reader(reader).expect("Failed to parse YAML"); - - Ok(config) -} - -#[derive(Debug)] -pub struct ConfigTask { - tag: String, - command: String -} - -#[derive(Debug)] -pub struct SubConfig { - config: Config, - path: PathBuf, -} - -#[derive(Debug)] -pub struct Config { - name: String, - tasks: Vec, - sub_configs: Vec, -} - -pub fn parse_config(path_buf: &PathBuf) -> Result { - let config_file = read_config_file(path_buf)?; - - let name = config_file.name; - - let tasks = config_file.tasks - .iter() - .map(|(tag, command)| ConfigTask{tag: tag.clone(), command: command.clone()}) - .collect(); - - let mut sub_configs: Vec = Vec::new(); - - let parent_dir = path_buf.parent().ok_or("Failed to get parent directory")?; - - for directory in config_file.directories { - let mut pattern: PathBuf = parent_dir.to_path_buf(); - pattern.push(&directory); - if !pattern.ends_with(".yaml") { - pattern.push("rask.yaml"); - } - - for entry in glob(pattern.to_str().unwrap()).map_err(|e| format!("Failed to read glob pattern: {}", e))? { - if let Ok(config_path) = entry { - let sub_config = SubConfig { - config: parse_config(&config_path)?, - path: config_path, - }; - sub_configs.push(sub_config); - } - } - } - - let config = Config{name, tasks, sub_configs}; - - Ok(config) -} \ No newline at end of file diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 6eb0168..a5592b5 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,2 +1,2 @@ pub mod file_resolvers; -pub mod config_reader; +pub mod config;