use std::collections::HashSet;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum EdgeKind {
Directed,
Incoming,
Undirected,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Edge {
pub target: String,
pub kind: EdgeKind,
pub label: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Node {
pub path: String,
pub edges: Vec<Edge>,
}
impl Node {
pub fn new(path: impl Into<String>) -> Self {
Self {
path: path.into(),
edges: Vec::new(),
}
}
pub fn is_orphan(&self) -> bool {
self.edges.is_empty()
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Graph {
pub nodes: Vec<Node>,
}
impl Graph {
pub fn new() -> Self {
Self::default()
}
pub fn get(&self, path: &str) -> Option<&Node> {
self.nodes.iter().find(|n| n.path == path)
}
pub fn get_mut(&mut self, path: &str) -> Option<&mut Node> {
self.nodes.iter_mut().find(|n| n.path == path)
}
pub fn contains(&self, path: &str) -> bool {
self.nodes.iter().any(|n| n.path == path)
}
pub fn add_node(&mut self, node: Node) {
assert!(!self.contains(&node.path), "duplicate node: {}", node.path);
self.nodes.push(node);
}
pub fn orphans(&self) -> impl Iterator<Item = &Node> {
let targeted: HashSet<&str> = self
.nodes
.iter()
.flat_map(|n| n.edges.iter().map(|e| e.target.as_str()))
.collect();
self.nodes
.iter()
.filter(move |n| n.edges.is_empty() && !targeted.contains(n.path.as_str()))
}
pub fn dangling_edges(&self) -> Vec<(&Node, &Edge)> {
self.nodes
.iter()
.flat_map(|n| n.edges.iter().map(move |e| (n, e)))
.filter(|(_, e)| !self.contains(&e.target))
.collect()
}
}