diff options
author | Lars Wirzenius <liw@liw.fi> | 2019-09-23 10:45:45 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2019-09-23 10:45:45 +0300 |
commit | 804c418bbfba696020957f5f22aa30660302b744 (patch) | |
tree | ba296e779b1ba65faab49b9cce13cb7b10baff15 | |
parent | 2936f3cf609bff77ef0851ebf55b35c74d31a67a (diff) | |
download | roadmap-804c418bbfba696020957f5f22aa30660302b744.tar.gz |
Add: support for color and shape for steps
These must currently be specified in the YAML directly.
-rw-r--r-- | legend.svg | 100 | ||||
-rw-r--r-- | legend.yaml | 1 | ||||
-rw-r--r-- | src/bin/roadmap2dot.rs | 7 | ||||
-rw-r--r-- | src/lib.rs | 69 |
4 files changed, 122 insertions, 55 deletions
@@ -4,75 +4,75 @@ <!-- Generated by graphviz version 2.40.1 (20161225.0304) --> <!-- Title: roadmap Pages: 1 --> -<svg width="526pt" height="347pt" - viewBox="0.00 0.00 525.58 347.29" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> -<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 343.2864)"> +<svg width="497pt" height="503pt" + viewBox="0.00 0.00 496.58 503.09" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 499.0854)"> <title>roadmap</title> -<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-343.2864 521.5757,-343.2864 521.5757,4 -4,4"/> -<!-- goal --> +<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-499.0854 492.5757,-499.0854 492.5757,4 -4,4"/> +<!-- ready --> <g id="node1" class="node"> -<title>goal</title> -<ellipse fill="none" stroke="#000000" cx="217.066" cy="-48.0833" rx="98.0761" ry="48.1667"/> -<text text-anchor="middle" x="217.066" y="-66.8833" font-family="Times,serif" font-size="14.00" fill="#000000">This is the end goal:</text> -<text text-anchor="middle" x="217.066" y="-51.8833" font-family="Times,serif" font-size="14.00" fill="#000000">if we reach here, there</text> -<text text-anchor="middle" x="217.066" y="-36.8833" font-family="Times,serif" font-size="14.00" fill="#000000">is nothing more to be</text> -<text text-anchor="middle" x="217.066" y="-21.8833" font-family="Times,serif" font-size="14.00" fill="#000000">done in the project</text> +<title>ready</title> +<ellipse fill="#ffffff" stroke="#000000" cx="90.5097" cy="-457.6087" rx="90.5193" ry="37.4533"/> +<text text-anchor="middle" x="90.5097" y="-468.9087" font-family="Times,serif" font-size="14.00" fill="#000000">This task is ready </text> +<text text-anchor="middle" x="90.5097" y="-453.9087" font-family="Times,serif" font-size="14.00" fill="#000000">to be done: it is not</text> +<text text-anchor="middle" x="90.5097" y="-438.9087" font-family="Times,serif" font-size="14.00" fill="#000000">blocked by anything</text> </g> -<!-- finished --> -<g id="node2" class="node"> -<title>finished</title> -<ellipse fill="none" stroke="#000000" cx="106.066" cy="-180.2498" rx="106.1321" ry="48.1667"/> -<text text-anchor="middle" x="106.066" y="-199.0498" font-family="Times,serif" font-size="14.00" fill="#000000">This task is finished;</text> -<text text-anchor="middle" x="106.066" y="-184.0498" font-family="Times,serif" font-size="14.00" fill="#000000">the arrow indicates what</text> -<text text-anchor="middle" x="106.066" y="-169.0498" font-family="Times,serif" font-size="14.00" fill="#000000">follows this task (unless</text> -<text text-anchor="middle" x="106.066" y="-154.0498" font-family="Times,serif" font-size="14.00" fill="#000000">it's blocked)</text> +<!-- blocked --> +<g id="node4" class="node"> +<title>blocked</title> +<polygon fill="#f4bada" stroke="#000000" points="258.5097,-304.566 118.5097,-304.566 118.5097,-251.566 258.5097,-251.566 258.5097,-304.566"/> +<text text-anchor="middle" x="188.5097" y="-289.366" font-family="Times,serif" font-size="14.00" fill="#000000">This task is blocked</text> +<text text-anchor="middle" x="188.5097" y="-274.366" font-family="Times,serif" font-size="14.00" fill="#000000">and can't be done until</text> +<text text-anchor="middle" x="188.5097" y="-259.366" font-family="Times,serif" font-size="14.00" fill="#000000">something happens</text> </g> -<!-- finished->goal --> -<g id="edge1" class="edge"> -<title>finished->goal</title> -<path fill="none" stroke="#000000" d="M143.9555,-135.1352C153.2427,-124.0771 163.2638,-112.145 172.8203,-100.7662"/> -<polygon fill="#000000" stroke="#000000" points="175.5962,-102.9031 179.3473,-92.9945 170.2359,-98.4012 175.5962,-102.9031"/> +<!-- ready->blocked --> +<g id="edge3" class="edge"> +<title>ready->blocked</title> +<path fill="none" stroke="#000000" d="M110.5779,-420.8423C127.6802,-389.5098 152.1083,-344.7558 169.0375,-313.7404"/> +<polygon fill="#000000" stroke="#000000" points="172.3152,-315.0405 174.0342,-304.5861 166.1709,-311.6868 172.3152,-315.0405"/> +</g> +<!-- goal --> +<g id="node2" class="node"> +<title>goal</title> +<polygon fill="#00eeee" stroke="#000000" points="285.5097,-136 146.5097,-68 285.5097,0 424.5097,-68 285.5097,-136"/> +<text text-anchor="middle" x="285.5097" y="-86.8" font-family="Times,serif" font-size="14.00" fill="#000000">This is the end goal:</text> +<text text-anchor="middle" x="285.5097" y="-71.8" font-family="Times,serif" font-size="14.00" fill="#000000">if we reach here, there</text> +<text text-anchor="middle" x="285.5097" y="-56.8" font-family="Times,serif" font-size="14.00" fill="#000000">is nothing more to be</text> +<text text-anchor="middle" x="285.5097" y="-41.8" font-family="Times,serif" font-size="14.00" fill="#000000">done in the project</text> </g> <!-- next --> <g id="node3" class="node"> <title>next</title> -<ellipse fill="none" stroke="#000000" cx="231.066" cy="-301.8097" rx="87.8629" ry="26.7407"/> -<text text-anchor="middle" x="231.066" y="-305.6097" font-family="Times,serif" font-size="14.00" fill="#000000">This task is chosen </text> -<text text-anchor="middle" x="231.066" y="-290.6097" font-family="Times,serif" font-size="14.00" fill="#000000">to be done next</text> -</g> -<!-- blocked --> -<g id="node4" class="node"> -<title>blocked</title> -<ellipse fill="none" stroke="#000000" cx="329.066" cy="-180.2498" rx="98.9899" ry="37.4533"/> -<text text-anchor="middle" x="329.066" y="-191.5498" font-family="Times,serif" font-size="14.00" fill="#000000">This task is blocked</text> -<text text-anchor="middle" x="329.066" y="-176.5498" font-family="Times,serif" font-size="14.00" fill="#000000">and can't be done until</text> -<text text-anchor="middle" x="329.066" y="-161.5498" font-family="Times,serif" font-size="14.00" fill="#000000">something happens</text> +<ellipse fill="#0cc000" stroke="#000000" cx="286.5097" cy="-457.6087" rx="87.8629" ry="26.7407"/> +<text text-anchor="middle" x="286.5097" y="-461.4087" font-family="Times,serif" font-size="14.00" fill="#000000">This task is chosen </text> +<text text-anchor="middle" x="286.5097" y="-446.4087" font-family="Times,serif" font-size="14.00" fill="#000000">to be done next</text> </g> <!-- next->blocked --> <g id="edge4" class="edge"> <title>next->blocked</title> -<path fill="none" stroke="#000000" d="M252.3306,-275.433C264.3282,-260.551 279.6337,-241.5659 293.4144,-224.4724"/> -<polygon fill="#000000" stroke="#000000" points="296.4842,-226.241 300.0357,-216.2592 291.0346,-221.8476 296.4842,-226.241"/> +<path fill="none" stroke="#000000" d="M271.8381,-430.7293C254.8296,-399.5686 226.7316,-348.0911 207.9257,-313.6375"/> +<polygon fill="#000000" stroke="#000000" points="210.9846,-311.9362 203.1213,-304.8355 204.8403,-315.29 210.9846,-311.9362"/> </g> <!-- blocked->goal --> <g id="edge2" class="edge"> <title>blocked->goal</title> -<path fill="none" stroke="#000000" d="M298.7572,-144.4836C287.3877,-131.0669 274.2143,-115.5215 261.8095,-100.8832"/> -<polygon fill="#000000" stroke="#000000" points="264.1487,-98.2297 255.0134,-92.8634 258.8083,-102.7553 264.1487,-98.2297"/> +<path fill="none" stroke="#000000" d="M200.7948,-251.461C214.5104,-221.7581 237.2033,-172.6136 255.6517,-132.6612"/> +<polygon fill="#000000" stroke="#000000" points="258.8646,-134.052 259.8793,-123.5058 252.5094,-131.1174 258.8646,-134.052"/> </g> -<!-- ready --> +<!-- finished --> <g id="node5" class="node"> -<title>ready</title> -<ellipse fill="none" stroke="#000000" cx="427.066" cy="-301.8097" rx="90.5193" ry="37.4533"/> -<text text-anchor="middle" x="427.066" y="-313.1097" font-family="Times,serif" font-size="14.00" fill="#000000">This task is ready </text> -<text text-anchor="middle" x="427.066" y="-298.1097" font-family="Times,serif" font-size="14.00" fill="#000000">to be done: it is not</text> -<text text-anchor="middle" x="427.066" y="-283.1097" font-family="Times,serif" font-size="14.00" fill="#000000">blocked by anything</text> +<title>finished</title> +<ellipse fill="#eeeeee" stroke="#000000" cx="382.5097" cy="-278.066" rx="106.1321" ry="106.1321"/> +<text text-anchor="middle" x="382.5097" y="-296.866" font-family="Times,serif" font-size="14.00" fill="#000000">This task is finished;</text> +<text text-anchor="middle" x="382.5097" y="-281.866" font-family="Times,serif" font-size="14.00" fill="#000000">the arrow indicates what</text> +<text text-anchor="middle" x="382.5097" y="-266.866" font-family="Times,serif" font-size="14.00" fill="#000000">follows this task (unless</text> +<text text-anchor="middle" x="382.5097" y="-251.866" font-family="Times,serif" font-size="14.00" fill="#000000">it's blocked)</text> </g> -<!-- ready->blocked --> -<g id="edge3" class="edge"> -<title>ready->blocked</title> -<path fill="none" stroke="#000000" d="M398.1928,-265.9952C387.6207,-252.8814 375.5356,-237.8909 364.4582,-224.1505"/> -<polygon fill="#000000" stroke="#000000" points="367.0726,-221.8168 358.0715,-216.2284 361.623,-226.2102 367.0726,-221.8168"/> +<!-- finished->goal --> +<g id="edge1" class="edge"> +<title>finished->goal</title> +<path fill="none" stroke="#000000" d="M337.9842,-181.6404C330.3953,-165.2057 322.6758,-148.488 315.5755,-133.1114"/> +<polygon fill="#000000" stroke="#000000" points="318.6112,-131.3367 311.2413,-123.7251 312.256,-134.2713 318.6112,-131.3367"/> </g> </g> </svg> diff --git a/legend.yaml b/legend.yaml index 9bb3be7..e23663c 100644 --- a/legend.yaml +++ b/legend.yaml @@ -1,4 +1,5 @@ goal: + status: goal label: | This is the end goal: if we reach here, there diff --git a/src/bin/roadmap2dot.rs b/src/bin/roadmap2dot.rs index 16ec0f0..2526555 100644 --- a/src/bin/roadmap2dot.rs +++ b/src/bin/roadmap2dot.rs @@ -33,8 +33,15 @@ 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!(""); println!("{}", r.as_dot(LABEL_WIDTH).unwrap()); Ok(()) @@ -38,6 +38,7 @@ use textwrap::fill; #[derive(Clone)] pub struct Step { name: String, + status: String, label: String, depends: Vec<String>, } @@ -47,6 +48,7 @@ impl Step { pub fn new(name: &str, label: &str) -> Step { Step { name: name.to_string(), + status: "".to_string(), label: label.to_string(), depends: vec![], } @@ -62,6 +64,17 @@ impl Step { &self.label } + /// Return the status of a step. + pub fn status<'a>(&'a self) -> &'a str { + &self.status + } + + /// Set the status of a step. + pub fn set_status(&mut self, status: &str) { + eprintln!("xxx setting status to {} for {}", status, self.label()); + self.status = String::from(status); + } + /// Return vector of names of dependencies for a step. pub fn dependencies(&self) -> impl Iterator<Item = &String> { self.depends.iter() @@ -69,7 +82,7 @@ impl Step { /// Add the name of a dependency to step. pub fn add_dependency(&mut self, name: &str) { - self.depends.push(name.to_string()); + self.depends.push(String::from(name)); } } @@ -94,18 +107,30 @@ impl Roadmap { roadmap.add_step(&step)?; } + for name in roadmap.step_names() { + let step = roadmap.get_step(name).unwrap(); + eprintln!("roadmap step {} ({}) status {}", name, step.name(), step.status()); + } + Ok(roadmap) } // Convert a Value into a Step, if possible. fn step_from_value(name: &str, value: &Value) -> Result<Step, &'static str> { let label_key = Value::String("label".to_string()); + let status_key = Value::String("status".to_string()); let deps_key = Value::String("depends".to_string()); + eprintln!("=====\nstep_from_value: {:?}", value); match value { Value::Mapping(map) => { if let Some(Value::String(label)) = map.get(&label_key) { let mut step = Step::new(name, label); + if let Some(Value::String(status)) = map.get(&status_key) { + eprintln!("step_from_value: status: {:?}", status); + step.set_status(&status); + eprintln!("status from step: {:?}", step.status()); + } if let Some(Value::Sequence(deps)) = map.get(&deps_key) { for depname in deps.iter() { if let Value::String(depname) = depname { @@ -163,9 +188,11 @@ impl Roadmap { pub fn as_dot(self, label_width: usize) -> Result<String, Box<dyn std::error::Error>> { let labels = self.steps.iter().map(|step| { format!( - "{} [label=\"{}\"];\n", + "{} [label=\"{}\" style=filled fillcolor=\"{}\" shape=\"{}\"];\n", step.name(), - fill(&step.label(), label_width).replace("\n", "\\n") + fill(&step.label(), label_width).replace("\n", "\\n"), + Roadmap::get_status_color(step), + Roadmap::get_status_shape(step), ) }); @@ -186,6 +213,30 @@ impl Roadmap { Ok(dot) } + + fn get_status_color(step: &Step) -> &str { +// eprintln!("color for {}: {}", step.status(), step.label()); + match step.status() { + "blocked" => "#f4bada", + "finished" => "#eeeeee", + "ready" => "#ffffff", + "next" => "#0cc00", + "goal" => "#00eeee", + _ => "unknownstatus", + } + } + + fn get_status_shape(step: &Step) -> &str { +// eprintln!("shape for {}", step.status()); + match step.status() { + "blocked" => "rectangle", + "finished" => "circle", + "ready" => "ellipse", + "next" => "ellipse", + "goal" => "diamond", + _ => "unknownshape", + } + } } #[cfg(test)] @@ -196,11 +247,19 @@ mod tests { fn new_step() { let step = Step::new("myname", "my label"); assert_eq!(step.name(), "myname"); + assert_eq!(step.status(), "goal"); assert_eq!(step.label(), "my label"); assert_eq!(step.dependencies().count(), 0); } #[test] + fn set_status() { + let mut step = Step::new("myname", "my label"); + step.set_status("next"); + assert_eq!(step.status(), "next"); + } + + #[test] fn add_step_dependency() { let mut second = Step::new("second", "the second step"); second.add_dependency("first"); @@ -255,8 +314,8 @@ mod tests { assert_eq!( roadmap.as_dot(999).unwrap(), "digraph \"roadmap\" { -first [label=\"\"]; -second [label=\"\"]; +first [label=\"\" fillcolor=\"#ffffff\" shape=\"ellipse\"]; +second [label=\"\" fillcolor=\"#00eeeeee\" shape=\"diamond\"]; first -> second; } " |