summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2019-09-23 10:45:45 +0300
committerLars Wirzenius <liw@liw.fi>2019-09-23 10:45:45 +0300
commit804c418bbfba696020957f5f22aa30660302b744 (patch)
treeba296e779b1ba65faab49b9cce13cb7b10baff15
parent2936f3cf609bff77ef0851ebf55b35c74d31a67a (diff)
downloadroadmap-804c418bbfba696020957f5f22aa30660302b744.tar.gz
Add: support for color and shape for steps
These must currently be specified in the YAML directly.
-rw-r--r--legend.svg100
-rw-r--r--legend.yaml1
-rw-r--r--src/bin/roadmap2dot.rs7
-rw-r--r--src/lib.rs69
4 files changed, 122 insertions, 55 deletions
diff --git a/legend.svg b/legend.svg
index cbd540f..549ac01 100644
--- a/legend.svg
+++ b/legend.svg
@@ -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&#39;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&#39;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&#45;&gt;goal -->
-<g id="edge1" class="edge">
-<title>finished&#45;&gt;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&#45;&gt;blocked -->
+<g id="edge3" class="edge">
+<title>ready&#45;&gt;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&#39;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&#45;&gt;blocked -->
<g id="edge4" class="edge">
<title>next&#45;&gt;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&#45;&gt;goal -->
<g id="edge2" class="edge">
<title>blocked&#45;&gt;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&#39;s blocked)</text>
</g>
-<!-- ready&#45;&gt;blocked -->
-<g id="edge3" class="edge">
-<title>ready&#45;&gt;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&#45;&gt;goal -->
+<g id="edge1" class="edge">
+<title>finished&#45;&gt;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(())
diff --git a/src/lib.rs b/src/lib.rs
index 3058373..91938b4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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;
}
"