diff --git a/techtree-manager/src/tree.rs b/techtree-manager/src/tree.rs index f87873c..bc3e343 100644 --- a/techtree-manager/src/tree.rs +++ b/techtree-manager/src/tree.rs @@ -15,8 +15,20 @@ pub struct Element { pub status: ElementStatus, } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum ElementRole { + Root, + Ultimate, + Intermediate, + Disjoint, +} + impl Element { - fn to_dot_node_attributes(&self, role: Option) -> String { + fn to_dot_node_attributes( + &self, + role: Option, + subtree_role: Option, + ) -> String { let Element { issue_number, description, @@ -31,10 +43,18 @@ impl Element { )); attributes.push(r#"shape = "record""#.to_owned()); - let (color, background) = match (role, status) { + let (color, background) = match (subtree_role, status) { (Some(SubtreeElementRole::ElementOfInterest), _) => ("black", "white"), (Some(SubtreeElementRole::Dependant), _) => ("gray", "gray"), - (_, ElementStatus::Missing) => ("#800", "#fcc"), + (_, ElementStatus::Missing) => ( + "#800", + // Highlight root elements + if role == Some(ElementRole::Root) { + "#ffddc1" + } else { + "#fcc" + }, + ), (_, ElementStatus::Assigned) => ("#a50", "#ffa"), (_, ElementStatus::Completed) => ("#080", "#afa"), }; @@ -170,7 +190,30 @@ impl Tree { }) } + pub fn get_element_role(&self, element: ElementIndex) -> ElementRole { + let has_dependencies = self + .graph + .neighbors_directed(element, petgraph::Direction::Outgoing) + .next() + .is_some(); + let has_dependants = self + .graph + .neighbors_directed(element, petgraph::Direction::Incoming) + .next() + .is_some(); + + match (has_dependencies, has_dependants) { + (false, true) => ElementRole::Root, + (true, false) => ElementRole::Ultimate, + (true, true) => ElementRole::Intermediate, + (false, false) => ElementRole::Disjoint, + } + } + pub fn to_dot(&self) -> String { + let to_node_attributes = |_g, (id, element): (ElementIndex, &Element)| { + element.to_dot_node_attributes(Some(self.get_element_role(id)), None) + }; let dot = petgraph::dot::Dot::with_attr_getters( &self.graph, &[ @@ -180,7 +223,7 @@ impl Tree { petgraph::dot::Config::GraphContentOnly, ], &|_g, _edge_id| "".to_string(), - &|_g, (_, element)| element.to_dot_node_attributes(None), + &to_node_attributes, ); let ultimate_elements: Vec<_> = self @@ -318,7 +361,11 @@ impl<'a> Subtree<'a> { petgraph::dot::Config::RankDir(petgraph::dot::RankDir::BT), ], &|_g, _edge_id| "".to_string(), - &|_g, (_, element)| element.element.to_dot_node_attributes(Some(element.role)), + &|_g, (_, element)| { + element + .element + .to_dot_node_attributes(None, Some(element.role)) + }, ); format!("{:?}", dot)