use std::collections::HashMap;
use petgraph::graph::{DiGraph, NodeIndex};
use petgraph::visit::EdgeRef;
use serde::{Deserialize, Serialize};
use crate::error::CorrelationError;
use crate::model::NodeKind;
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct SuiteNode {
pub id: String,
pub kind: NodeKind,
pub label: String,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SuiteEdge {
DependsOn,
Approves,
Mentions,
}
#[derive(Debug, Default)]
pub struct SuiteGraph {
graph: DiGraph<SuiteNode, SuiteEdge>,
index: HashMap<String, NodeIndex>,
}
impl SuiteGraph {
pub fn add_node(&mut self, node: SuiteNode) -> NodeIndex {
if let Some(&existing) = self.index.get(&node.id) {
self.graph[existing] = node;
return existing;
}
let id = node.id.clone();
let idx = self.graph.add_node(node);
self.index.insert(id, idx);
idx
}
pub fn add_edge(
&mut self,
from: &str,
to: &str,
edge: SuiteEdge,
) -> Result<(), CorrelationError> {
let from_idx = self
.index
.get(from)
.copied()
.ok_or_else(|| CorrelationError::UnknownEdgeTarget(from.to_string()))?;
let to_idx = self
.index
.get(to)
.copied()
.ok_or_else(|| CorrelationError::UnknownEdgeTarget(to.to_string()))?;
self.graph.add_edge(from_idx, to_idx, edge);
Ok(())
}
pub(crate) fn idx(&self, id: &str) -> Option<NodeIndex> {
self.index.get(id).copied()
}
pub(crate) fn node(&self, idx: NodeIndex) -> &SuiteNode {
&self.graph[idx]
}
pub(crate) fn inbound(
&self,
idx: NodeIndex,
edges: &[SuiteEdge],
) -> Vec<(NodeIndex, SuiteEdge)> {
self.graph
.edges_directed(idx, petgraph::Incoming)
.filter(|e| edges.contains(e.weight()))
.map(|e| (e.source(), *e.weight()))
.collect()
}
pub fn node_count(&self) -> usize {
self.graph.node_count()
}
pub fn edge_count(&self) -> usize {
self.graph.edge_count()
}
}