use std::collections::{HashMap, VecDeque};
use super::resource::ResourceId;
use super::KvasirError;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct NodeKey(pub usize);
#[derive(Clone, Debug)]
pub struct Edge {
pub producer: NodeKey,
pub resource: ResourceId,
pub consumer: NodeKey,
}
#[derive(Default)]
pub struct KvasirGraph {
nodes: Vec<Box<dyn super::node::KvasirNode>>,
edges: Vec<Edge>,
adjacency: HashMap<NodeKey, Vec<(ResourceId, NodeKey)>>,
reverse_adj: HashMap<NodeKey, Vec<NodeKey>>,
next_key: usize,
}
impl KvasirGraph {
pub fn new() -> Self {
Self::default()
}
pub fn add_node(&mut self, node: Box<dyn super::node::KvasirNode>) -> NodeKey {
let key = NodeKey(self.next_key);
self.next_key += 1;
self.nodes.push(node);
key
}
pub fn connect(&mut self, from: NodeKey, resource: ResourceId, to: NodeKey) {
self.edges.push(Edge {
producer: from,
resource,
consumer: to,
});
self.adjacency
.entry(from)
.or_default()
.push((resource, to));
self.reverse_adj.entry(to).or_default().push(from);
}
pub fn topological_sort(&self) -> Result<Vec<NodeKey>, KvasirError> {
let n = self.nodes.len();
let mut in_degree: HashMap<NodeKey, usize> = HashMap::new();
for key in (0..n).map(NodeKey) {
in_degree.entry(key).or_insert(0);
}
for edge in &self.edges {
*in_degree.entry(edge.consumer).or_insert(0) += 1;
}
let mut queue: VecDeque<NodeKey> = in_degree
.iter()
.filter(|(_, d)| **d == 0)
.map(|(k, _)| *k)
.collect();
let mut order = Vec::with_capacity(n);
while let Some(node) = queue.pop_front() {
order.push(node);
if let Some(neighbors) = self.adjacency.get(&node) {
for (_, consumer) in neighbors {
let deg = in_degree.get_mut(consumer).expect("Graph integrity violation: dangling consumer edge");
*deg -= 1;
if *deg == 0 {
queue.push_back(*consumer);
}
}
}
}
if order.len() != n {
let cycle_nodes: Vec<String> = in_degree
.iter()
.filter(|(_, d)| **d > 0)
.filter_map(|(k, _)| self.node(*k).map(|n| n.label().to_string()))
.collect();
return Err(KvasirError::CycleDetected(cycle_nodes));
}
Ok(order)
}
pub fn node(&self, key: NodeKey) -> Option<&dyn super::node::KvasirNode> {
self.nodes.get(key.0).map(|b| b.as_ref())
}
pub fn to_dot(&self) -> String {
let mut s = String::from("digraph Kvasir {\n");
for (i, node) in self.nodes.iter().enumerate() {
s.push_str(&format!(" n{} [label=\"{}\"];\n", i, node.label()));
}
for edge in &self.edges {
s.push_str(&format!(
" n{} -> n{} [label=\"{:?}\"];\n",
edge.producer.0, edge.consumer.0, edge.resource
));
}
s.push_str("}\n");
s
}
}
pub struct GraphBuilder {
graph: KvasirGraph,
}
impl GraphBuilder {
pub fn new() -> Self {
Self {
graph: KvasirGraph::new(),
}
}
pub fn add_node(&mut self, node: Box<dyn super::node::KvasirNode>) -> NodeKey {
self.graph.add_node(node)
}
pub fn connect(&mut self, from: NodeKey, resource: ResourceId, to: NodeKey) {
self.graph.connect(from, resource, to);
}
pub fn build(self) -> KvasirGraph {
self.graph
}
}
impl Default for GraphBuilder {
fn default() -> Self {
Self::new()
}
}