use internment::Intern;
use petgraph::dot::Config;
use petgraph::dot::Dot;
use petgraph::prelude::DiGraphMap;
use petgraph::visit::IntoNodeReferences;
use crate::resolve::Target;
#[derive(Debug, Default)]
pub struct TargetGraph {
graph: DiGraphMap<Intern<Target>, ()>,
}
impl TargetGraph {
#[must_use]
pub fn inner(&self) -> &DiGraphMap<Intern<Target>, ()> {
&self.graph
}
pub fn inner_mut(&mut self) -> &mut DiGraphMap<Intern<Target>, ()> {
&mut self.graph
}
pub fn add_node(&mut self, node: Intern<Target>) {
self.graph.add_node(node);
}
pub fn add_edge(&mut self, parent: Intern<Target>, child: Intern<Target>) {
self.graph.add_edge(parent, child, ());
}
#[must_use]
pub fn nodes(&self) -> Vec<Intern<Target>> {
self.graph.nodes().collect()
}
#[must_use]
pub fn leaf_targets(&self) -> Vec<Intern<Target>> {
self.graph
.node_references()
.filter(|(node, _)| {
self.graph
.neighbors_directed(*node, petgraph::Direction::Outgoing)
.count()
== 0
})
.map(|(node, _)| node)
.collect()
}
#[must_use]
pub fn graphviz(&self) -> String {
format!(
"{:?}",
Dot::with_config(&self.graph, &[Config::EdgeNoLabel])
)
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::*;
#[test]
fn test_leaf_nodes() {
let mut g = TargetGraph::default();
let parent = Target::from_str("10.0.0.0/24").unwrap().intern();
let child1 = Target::from_str("10.0.0.1").unwrap().intern();
let child2 = Target::from_str("10.0.0.2").unwrap().intern();
g.add_edge(parent, child1);
g.add_edge(parent, child2);
let got = g.leaf_targets();
let should = vec![child1, child2];
assert_eq!(got, should);
}
}