summaryrefslogtreecommitdiff
path: root/src/map.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/map.rs')
-rw-r--r--src/map.rs46
1 files changed, 40 insertions, 6 deletions
diff --git a/src/map.rs b/src/map.rs
index 4ed4c7f..ab27164 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -4,6 +4,11 @@ pub use crate::from_yaml;
pub use crate::Status;
pub use crate::Step;
+
+/// Error in Roadmap, from parsing or otherwise.
+pub type RoadmapError<T> = Result<T, String>;
+
+
/// Represent a full project roadmap.
///
/// This stores all the steps needed to reach the end goal. See the
@@ -116,10 +121,41 @@ impl Roadmap {
self.steps.iter().all(|other| !other.depends_on(step.name()))
}
+
+ // Validate that the parsed, constructed roadmap is valid.
+ pub fn validate(&self) -> RoadmapError<()> {
+ // Is there exactly one goal?
+ match self.count_goals() {
+ 0 => return Err(format!("the roadmap doesn't have a goal")),
+ 1 => (),
+ _ => return Err(format!("must have exactly one goal for roadmap")),
+ }
+
+ // 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
+ ))
+ }
+ Some(_) => (),
+ }
+ }
+ }
+
+ Ok(())
+ }
+
/// 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.
pub fn format_as_dot(&self, label_width: usize) -> Result<String, Box<dyn std::error::Error>> {
+ self.validate()?;
+
let labels = self.steps.iter().map(|step| {
format!(
"{} [label=\"{}\" style=filled fillcolor=\"{}\" shape=\"{}\"];\n",
@@ -237,12 +273,10 @@ blocked:
#[test]
fn empty_dot() {
let roadmap = Roadmap::new();
- assert_eq!(
- roadmap.format_as_dot(999).unwrap(),
- "digraph \"roadmap\" {
-}
-"
- );
+ match roadmap.format_as_dot(999) {
+ Err(_) => (),
+ _ => panic!("expected error for empty roadmap"),
+ }
}
#[test]