diff --git a/Cargo.lock b/Cargo.lock index 363237f..38ee7c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + [[package]] name = "cfg-if" version = "1.0.0" @@ -12,6 +18,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "createnv" version = "0.1.0" dependencies = [ + "anyhow", "rand", ] diff --git a/Cargo.toml b/Cargo.toml index cd51bd1..c4ebb1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.71" rand = "0.8.5" diff --git a/src/main.rs b/src/main.rs index 8e0791d..b9eb2c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,27 +1,59 @@ -use crate::model::{Block, Comment, SimpleVariable, Variable}; +use std::env::args; +use std::io::{stdout, BufWriter}; + +use anyhow::Result; + +use crate::model::{ + AutoGeneratedVariable, Block, Comment, SimpleVariable, VariableWithDefaultValue, + VariableWithRandomValue, Variables, +}; mod model; -fn main() { +fn main() -> Result<()> { + if let Some(path) = args().nth(1) { + let out = BufWriter::new(stdout()); + let mut parser = Parser::new(out); + parser.parse(path)?; + return Ok(()); + } + let title = Comment { contents: "42".to_string(), }; let description = Some(Comment { contents: "Fourty-two".to_string(), }); - let variable1 = Box::new(SimpleVariable { + let variable1 = Variables::Simple(SimpleVariable { name: "ANSWER".to_string(), input: "42".to_string(), - }) as Box; - let variable2 = Box::new(SimpleVariable { + }); + let variable2 = Variables::Simple(SimpleVariable { name: "AS_TEXT".to_string(), input: "fourty two".to_string(), - }) as Box; - let variables = vec![variable1, variable2]; - let block = Block { - title, - description, - variables, - }; - println!("{}", block) + }); + let variable3 = Variables::DefaultValue(VariableWithDefaultValue { + name: "DEFAULT_VALUE_ONE".to_string(), + input: "".to_string(), + default: "default".to_string(), + }); + let variable4 = Variables::DefaultValue(VariableWithDefaultValue { + name: "DEFAULT_VALUE_ONE".to_string(), + input: "custom".to_string(), + default: "default".to_string(), + }); + let variable5 = Variables::Random(VariableWithRandomValue { + name: "SECRET_KEY".to_string(), + length: None, + }); + let variable6 = Variables::AutoGenerated(AutoGeneratedVariable::new( + "AUTO_GENERATED".to_string(), + "{ANSWER}-{DEFAULT_VALUE_ONE}".to_string(), + )); + let variables = vec![ + variable1, variable2, variable3, variable4, variable5, variable6, + ]; + let block = Block::new(title, description, variables); + println!("{block}"); + Ok(()) } diff --git a/src/model.rs b/src/model.rs index be1f556..3230ae9 100644 --- a/src/model.rs +++ b/src/model.rs @@ -40,7 +40,7 @@ impl Variable for SimpleVariable { pub struct VariableWithDefaultValue { pub name: String, pub default: String, - pub input: Option, + pub input: String, } impl Variable for VariableWithDefaultValue { @@ -48,9 +48,10 @@ impl Variable for VariableWithDefaultValue { self.name.to_string() } fn value(&self) -> String { - match &self.input { - Some(value) => value.to_string(), - None => self.default.to_string(), + if self.input.is_empty() { + self.default.to_string() + } else { + self.input.to_string() } } } @@ -58,7 +59,24 @@ impl Variable for VariableWithDefaultValue { pub struct AutoGeneratedVariable { pub name: String, pub pattern: String, - pub settings: HashMap<&'static str, &'static str>, + + context: HashMap, +} + +impl AutoGeneratedVariable { + pub fn new(name: String, pattern: String) -> Self { + Self { + name, + pattern, + context: HashMap::new(), + } + } + + pub fn load_context(&mut self, ctx: &HashMap) { + for (k, v) in ctx.iter() { + self.context.insert(k.to_string(), v.to_string()); + } + } } impl Variable for AutoGeneratedVariable { @@ -67,9 +85,9 @@ impl Variable for AutoGeneratedVariable { } fn value(&self) -> String { let mut value: String = self.pattern.to_string(); - for (k, v) in self.settings.iter() { + for (k, v) in self.context.iter() { let key = format!("{{{}}}", *k); - value = value.replace(&key, *v); + value = value.replace(&key, v); } value } @@ -100,10 +118,54 @@ impl Variable for VariableWithRandomValue { } } +pub enum Variables { + Simple(SimpleVariable), + DefaultValue(VariableWithDefaultValue), + AutoGenerated(AutoGeneratedVariable), + Random(VariableWithRandomValue), +} + pub struct Block { pub title: Comment, pub description: Option, - pub variables: Vec>, + pub variables: Vec, + + context: HashMap, +} + +impl Block { + pub fn new(title: Comment, description: Option, variables: Vec) -> Self { + let context: HashMap = HashMap::new(); + let has_auto_generated_variables = variables + .iter() + .any(|v| matches!(v, Variables::AutoGenerated(_))); + + let mut block = Self { + title, + description, + variables, + context, + }; + + if has_auto_generated_variables { + for variable in &block.variables { + match variable { + Variables::Simple(var) => block.context.insert(var.key(), var.value()), + Variables::DefaultValue(var) => block.context.insert(var.key(), var.value()), + Variables::AutoGenerated(_) => None, + Variables::Random(var) => block.context.insert(var.key(), var.value()), + }; + } + + for variable in &mut block.variables { + if let Variables::AutoGenerated(var) = variable { + var.load_context(&block.context); + } + } + } + + block + } } impl fmt::Display for Block { @@ -113,9 +175,16 @@ impl fmt::Display for Block { Some(desc) => lines.push(desc.to_string()), None => (), } + for variable in &self.variables { - lines.push(variable.to_string()); + match variable { + Variables::Simple(var) => lines.push(var.to_string()), + Variables::DefaultValue(var) => lines.push(var.to_string()), + Variables::AutoGenerated(var) => lines.push(var.to_string()), + Variables::Random(var) => lines.push(var.to_string()), + } } + write!(f, "{}", lines.join("\n")) } } @@ -146,7 +215,7 @@ mod tests { let line = VariableWithDefaultValue { name: "ANSWER".to_string(), default: "42".to_string(), - input: None, + input: "".to_string(), }; assert_eq!(line.to_string(), "ANSWER=42") } @@ -156,21 +225,27 @@ mod tests { let line = VariableWithDefaultValue { name: "ANSWER".to_string(), default: "42".to_string(), - input: Some("Fourty-two".to_string()), + input: "Fourty-two".to_string(), }; assert_eq!(line.to_string(), "ANSWER=Fourty-two") } #[test] fn test_auto_generated_variable() { - let mut settings = HashMap::new(); - settings.insert("first", "Fourty"); - settings.insert("second", "two"); - let line = AutoGeneratedVariable { - name: "ANSWER".to_string(), - pattern: "{first}-{second}".to_string(), - settings, + let mut line = + AutoGeneratedVariable::new("ANSWER".to_string(), "{first}-{second}".to_string()); + let first = SimpleVariable { + name: "first".to_string(), + input: "Fourty".to_string(), + }; + let second = SimpleVariable { + name: "second".to_string(), + input: "two".to_string(), }; + let mut ctx = HashMap::new(); + ctx.insert(first.key(), first.value()); + ctx.insert(second.key(), second.value()); + line.load_context(&ctx); assert_eq!(line.to_string(), "ANSWER=Fourty-two") } @@ -209,20 +284,16 @@ mod tests { let description = Some(Comment { contents: "Fourty-two".to_string(), }); - let variable1 = Box::new(SimpleVariable { + let variable1 = Variables::Simple(SimpleVariable { name: "ANSWER".to_string(), input: "42".to_string(), - }) as Box; - let variable2 = Box::new(SimpleVariable { + }); + let variable2 = Variables::Simple(SimpleVariable { name: "AS_TEXT".to_string(), input: "fourty two".to_string(), - }) as Box; + }); let variables = vec![variable1, variable2]; - let block = Block { - title, - description, - variables, - }; + let block = Block::new(title, description, variables); let got = block.to_string(); assert_eq!(got, "# 42\n# Fourty-two\nANSWER=42\nAS_TEXT=fourty two") }