use serde::{Deserialize, Serialize}; use serde_yml; use std::fs; use pad::PadStr; #[derive(Deserialize, Serialize, Debug)] enum UserConfigItem { String { name: String, max_length: u64 }, IPv4 { name: String, }, Boolean { name: String }, Number { name: String, max: Option, min: Option }, Submenu(UserConfigData) } #[derive(Deserialize, Serialize, Debug)] struct UserConfigData { name: String, config_items: Vec } #[derive(thiserror::Error, Debug)] pub enum ConfigotronError { #[error("IO Operation failed: {}", ._0.to_string())] IoError(#[from] std::io::Error), #[error("Failed to parse YAML File: {}", ._0.to_string())] YamlParsingError(#[from] serde_yml::Error), #[error("Missing field {} (line {} col {}) {}", .missing_filed, .line, .col, .context)] MissingFieldError{ col: i32, line: i32, missing_filed: Box, context: Box } } fn read_and_parse_yaml_file(input_file: &str) -> Result { let file_content = fs::read_to_string(&input_file)?; let config: UserConfigData = serde_yml::from_str(&file_content)?; return Ok(config); } fn generate_text_report_from_item(item: &UserConfigItem) -> Vec { return match item { UserConfigItem::Boolean {name} => vec![String::from(name), String::from("[y/n]")] , UserConfigItem::IPv4 { name } => vec![String::from(name), format!("[ipv4]")], UserConfigItem::String { name, max_length } => vec![String::from(name), format!("[text, max length {}]", max_length)], UserConfigItem::Number { name, min, max } => match (min, max) { (None, None) => vec![String::from(name), String::from("[number]")], (None, Some(maximum)) => vec![String::from(name), format!("[number, ..{}]", maximum)], (Some(minmum), None) => vec![String::from(name), format!("[number, {}..]", minmum)], (Some(minmum), Some(maximum)) => vec![String::from(name), format!("[number, {}..{}]", minmum, maximum)], }, UserConfigItem::Submenu(c) => generate_text_report(c) } } fn generate_text_report(config: &UserConfigData) -> Vec { let lines: Vec> = config.config_items .iter() .map(|i| generate_text_report_from_item(i)) .collect(); let block_width = lines .iter() .map(|l: &Vec| l .iter() .map(|s: &String| s.chars().count()) .max().unwrap_or(0) ) .max().unwrap_or(0); let line_seperator = format!("+{}+", "-".repeat(block_width + 2)); let config_name_width = config.name.chars().count() + 2; let mut lines_flattend: Vec = lines .iter() .map(|i| i .iter() .map(|s| format!("| {} |", s.pad_to_width(block_width))) .collect::>() ) .flat_map(|seg| [seg, vec![String::from(&line_seperator)]]) .flatten() .into_iter() .map(|e| format!("{}{}", " ".repeat(config_name_width), e)) .collect(); lines_flattend.insert(0, format!("{}{}", config.name.pad_to_width(config_name_width), line_seperator)); return lines_flattend; } fn main() { let data = match read_and_parse_yaml_file("example.yml") { Ok(d) => d, Err(error) => { println!("{}", error); return; } }; let res = generate_text_report(&data).join("\n"); println!("{}", res); println!("Hello, world!"); }