diff options
Diffstat (limited to 'src/map.rs')
-rw-r--r-- | src/map.rs | 40 |
1 files changed, 28 insertions, 12 deletions
@@ -1,12 +1,12 @@ -use std::error::Error; use textwrap::fill; pub use crate::from_yaml; +pub use crate::RoadmapError; pub use crate::Status; pub use crate::Step; /// Error in Roadmap, from parsing or otherwise. -pub type RoadmapResult<T> = Result<T, Box<dyn Error>>; +pub type RoadmapResult<T> = Result<T, RoadmapError>; /// Represent a full project roadmap. /// @@ -25,9 +25,17 @@ impl Roadmap { Roadmap { steps: vec![] } } + // Find steps that nothing depends on. + fn goals(&self) -> Vec<&Step> { + self.steps + .iter() + .filter(|step| self.is_goal(step)) + .collect() + } + /// Count number of steps that nothing depends on. pub fn count_goals(&self) -> usize { - self.steps.iter().filter(|step| self.is_goal(step)).count() + self.goals().len() } /// Iterate over step names. @@ -120,21 +128,29 @@ impl Roadmap { // Validate that the parsed, constructed roadmap is valid. pub fn validate(&self) -> RoadmapResult<()> { // Is there exactly one goal? - match self.count_goals() { - 0 => return Err(format!("the roadmap doesn't have a goal").into()), + let goals = self.goals(); + let n = goals.len(); + match n { + 0 => return Err(RoadmapError::NoGoals), 1 => (), - _ => return Err(format!("must have exactly one goal for roadmap").into()), + _ => { + let names: Vec<String> = goals.iter().map(|s| s.name().into()).collect(); + return Err(RoadmapError::ManyGoals { + count: n, + names: names, + }); + } } // Does every dependency exist? for step in self.iter() { for depname in step.dependencies() { match self.get_step(depname) { - None => { - return Err( - format!("step {} depends on missing {}", step.name(), depname).into(), - ) - } + None => + return Err(RoadmapError::MissingDep { + name: step.name().into(), + missing: depname.into(), + }), Some(_) => (), } } @@ -191,7 +207,7 @@ impl Roadmap { fn get_status_shape(step: &Step) -> &str { match step.status() { Status::Blocked => "rectangle", - Status::Finished => "circle", + Status::Finished => "octagon", Status::Ready => "ellipse", Status::Next => "ellipse", Status::Goal => "diamond", |