use std::collections::HashMap;
use petgraph::algo::toposort;
use petgraph::dot::{Config, Dot};
use petgraph::Graph;
use petgraph::graph::{EdgeIndex, Node, NodeIndex};
use petgraph::visit::{Control, depth_first_search, DfsEvent, Visitable, VisitMap};
use crate::task::Task;
#[derive(Clone)]
pub struct DependencyGraph {
inner: Graph<String, ()>,
nodes: HashMap<String, NodeIndex>,
}
impl DependencyGraph {
pub fn new() -> Self {
Self { inner: Default::default(), nodes: Default::default() }
}
pub fn retain_dependencies(&mut self, weights: Vec<String>) {
self.inner.reverse();
let mut vm = self.inner.visit_map();
for w in weights {
let node = self.find_or_add_node(w);
depth_first_search(&self.inner, Some(node), |event| {
if let DfsEvent::Discover(n, _time) = event {
if vm.is_visited(&n) { return Control::<NodeIndex>::Prune }
vm.visit(n);
}
Control::<NodeIndex>::Continue
});
}
self.inner.retain_nodes(|_fg, n| vm.is_visited(&n));
self.inner.reverse();
}
pub fn toposort(&self) -> Vec<String> {
toposort(&self.inner, None).unwrap().iter().map(|n| {
self.inner.node_weight(*n).unwrap().clone()
}).collect()
}
fn find_or_add_node(&mut self, weight: String) -> NodeIndex {
let entry = self.nodes.entry(weight.clone());
*entry.or_insert_with(|| self.inner.add_node(weight))
}
fn find_or_add_edge(&mut self, weight0: String, weight1: String) -> EdgeIndex {
let node0 = self.find_or_add_node(weight0);
let node1 = self.find_or_add_node(weight1);
self.inner.find_edge(node0, node1).unwrap_or_else(|| self.inner.add_edge(node0, node1, ()))
}
}
impl ToString for DependencyGraph {
fn to_string(&self) -> String {
format!("{:?}", Dot::with_config(&self.inner, &[Config::EdgeNoLabel]))
}
}
impl From<Vec<Task>> for DependencyGraph {
fn from(mut tasks: Vec<Task>) -> Self {
let mut g = Self::new();
for t in tasks.drain(..) {
for d in t.deps {
g.find_or_add_edge(d, t.name.clone());
}
}
g
}
}