use std::collections::HashMap; use std::fmt; use std::fmt::Debug; use std::io; use std::io::Write; #[derive(PartialEq, Debug)] pub struct Procedure { title: String, description: String, steps: Vec, } impl Procedure { pub fn new(title: &str, description: &str) -> Self { Procedure { title: title.to_string(), description: description.to_string(), steps: vec![], } } pub fn add_manual_step(mut self, title: &str, instructions: &str) -> Self { let new_step = Step::new_manual(title, instructions); self.steps.push(new_step); self } pub fn add_automated_step(mut self, title: &str, automation: F) -> Self where F: Fn(HashMap) -> HashMap + 'static, { let new_step = Step::new_automated(title, Box::new(automation)); self.steps.push(new_step); self } pub fn execute(self) { println!("# {}\n", self.title); println!("{}\n", self.description); print!("Press [Enter] to begin "); std::io::stdout().flush().expect("failed"); let mut _input: String = String::new(); io::stdin().read_line(&mut _input).expect("failed!"); println!(); let state = HashMap::new(); self.steps.iter().fold(state, handle_step); println!("done!"); } } fn handle_step(state: HashMap, step: &Step) -> HashMap { match step { Step::Automated(automated_step) => { println!("## {}\n", automated_step.title); (automated_step.automation)(state) } Step::Manual(manual_step) => { println!("## {}\n", manual_step.title); println!("{}\n", manual_step.instructions); print!("Press [Enter] to continue "); std::io::stdout().flush().expect("failed"); let mut _input: String = String::new(); io::stdin().read_line(&mut _input).expect("failed!"); println!(); state } } } #[derive(PartialEq, Debug)] enum Step { Manual(ManualStep), Automated(AutomatedStep), } impl Step { pub fn new_manual(title: &str, instructions: &str) -> Step { Step::Manual(ManualStep { title: title.to_string(), instructions: instructions.to_string(), }) } pub fn new_automated(title: &str, automation: Automation) -> Step { Step::Automated(AutomatedStep { title: title.to_string(), automation, }) } } #[derive(PartialEq, Debug)] struct ManualStep { title: String, instructions: String, } pub type Automation = Box) -> HashMap>; struct AutomatedStep { title: String, automation: Automation, } impl PartialEq for AutomatedStep { fn eq(&self, other: &Self) -> bool { self.title == other.title } } impl Debug for AutomatedStep { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AutomatedStep") .field("title", &self.title) .finish() } } #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { let p = Procedure::new("hello", "world"); assert_eq!( Procedure { title: "hello".to_string(), description: "world".to_string(), steps: vec![] }, p ); } #[test] fn procedure_with_manual_step() { let p = Procedure::new("hello", "world").add_manual_step("step", "instructions"); assert_eq!( Procedure { title: "hello".to_string(), description: "world".to_string(), steps: vec![Step::Manual(ManualStep { title: "step".to_string(), instructions: "instructions".to_string() })], }, p ); } }