diff options
author | Lars Wirzenius <liw@liw.fi> | 2019-09-24 11:01:46 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2019-09-24 12:07:05 +0300 |
commit | 52c6b4372ee27843443af88eee561b8e14bd7b76 (patch) | |
tree | 0e9c7a7ef675a5e45daeb41bfdf817f437b15da7 /src | |
parent | f01608de3b69d9a647ffdb4ca86a447d553a111f (diff) | |
download | roadmap-52c6b4372ee27843443af88eee561b8e14bd7b76.tar.gz |
Add: set_missing_statuses to set status of steps not set in input
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/roadmap2dot.rs | 10 | ||||
-rw-r--r-- | src/lib.rs | 102 |
2 files changed, 103 insertions, 9 deletions
diff --git a/src/bin/roadmap2dot.rs b/src/bin/roadmap2dot.rs index 2526555..25cbc34 100644 --- a/src/bin/roadmap2dot.rs +++ b/src/bin/roadmap2dot.rs @@ -33,15 +33,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { let mut text = String::new(); let mut f = File::open(opt.filename)?; f.read_to_string(&mut text)?; -// eprintln!("text:\n{}\n", text); - let r = Roadmap::from_yaml(&text)?; -// eprintln!(""); - for name in r.step_names() { - let step = r.get_step(name).unwrap(); - eprintln!("step {} ({}) status {}", name, step.name(), step.status()); - } - eprintln!(""); + let mut r = Roadmap::from_yaml(&text)?; + r.set_missing_statuses(); println!("{}", r.as_dot(LABEL_WIDTH).unwrap()); Ok(()) @@ -35,7 +35,7 @@ use std::collections::HashMap; use textwrap::fill; /// A step in a roadmap. -#[derive(Clone)] +#[derive(Clone,Debug,PartialEq)] pub struct Step { name: String, status: String, @@ -83,6 +83,11 @@ impl Step { pub fn add_dependency(&mut self, name: &str) { self.depends.push(String::from(name)); } + + /// Does this step depend on given other step? + pub fn depends_on(&self, other_name: &str) -> bool { + self.depends.iter().any(|depname| depname == other_name) + } } /// All the steps to get to the end goal. @@ -201,6 +206,70 @@ impl Roadmap { self.steps.iter_mut() } + /// Compute status of any step for which it has not been specified + /// in the input. + pub fn set_missing_statuses(&mut self) { + let new_steps: Vec<Step> = self.steps.iter() + .map(|step| { + let mut step = step.clone(); + if self.is_unset(&step) { + if self.is_goal(&step) { + step.set_status("goal"); + } else if self.is_blocked(&step) { + step.set_status("blocked"); + } else if self.is_ready(&step) { + step.set_status("ready"); + } + } + step + }) + .collect(); + + if self.steps != new_steps { + self.steps = new_steps; + self.set_missing_statuses(); + } + } + + // Is status unset? + fn is_unset(&self, step: &Step) -> bool { + step.status() == "" + } + + // Should unset status be ready? In other words, if there are any + // dependencies, they are all finished. + fn is_ready(&self, step: &Step) -> bool { + self.dep_statuses(step).iter().all(|status| status == &"finished") + } + + // Should unset status be blocked? In other words, if there are + // any dependencies, that aren't finished. + fn is_blocked(&self, step: &Step) -> bool { + self.dep_statuses(step).iter().any(|status| status != &"finished") + } + + // Return vector of all statuses of all dependencies + fn dep_statuses<'a>(&'a self, step: &Step) -> Vec<&'a str> { + step.dependencies() + .map(|depname| { + if let Some(step) = self.get_step(depname) { + step.status() + } else { + &"" + } + }) + .collect() + } + + // Should unset status be goal? In other words, does any other + // step depend on this one? + fn is_goal(&self, step: &Step) -> bool { + let has_parent = self.steps + .iter() + .any(|other| other.depends_on(step.name())); + !has_parent + } + /// Get a Graphviz dot language representation of a roadmap. This /// is the textual representation, and the caller needs to use the /// Graphviz dot(1) tool to create an image from it. @@ -310,6 +379,37 @@ mod tests { } #[test] + fn set_missing_goal_status() { + let mut r = Roadmap::from_yaml(" +goal: + depends: + - finished + - blocked + +finished: + status: finished + +ready: + depends: + - finished + +next: + status: next + +blocked: + depends: + - ready + - next +").unwrap(); + r.set_missing_statuses(); + assert_eq!(r.get_step("goal").unwrap().status(), "goal"); + assert_eq!(r.get_step("finished").unwrap().status(), "finished"); + assert_eq!(r.get_step("ready").unwrap().status(), "ready"); + assert_eq!(r.get_step("next").unwrap().status(), "next"); + assert_eq!(r.get_step("blocked").unwrap().status(), "blocked"); + } + + #[test] fn empty_dot() { let roadmap = Roadmap::new(); assert_eq!( |