use crate::graph::types::{Edge, EdgeKind, NodeId};
use std::collections::{HashMap, HashSet, VecDeque};
#[derive(Debug, Default)]
pub struct GraphStorage {
forward: HashMap<NodeId, Vec<(NodeId, EdgeKind, u32)>>,
reverse: HashMap<NodeId, Vec<NodeId>>,
}
impl GraphStorage {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn insert_edge(&mut self, edge: &Edge) {
let fwd = self.forward.entry(edge.from.clone()).or_default();
if !fwd
.iter()
.any(|(to, k, _)| *to == edge.to && *k == edge.kind)
{
fwd.push((edge.to.clone(), edge.kind, edge.line));
}
let rev = self.reverse.entry(edge.to.clone()).or_default();
if !rev.contains(&edge.from) {
rev.push(edge.from.clone());
}
}
pub fn remove_file_edges(&mut self, file: &str) {
let to_remove: Vec<NodeId> = self
.forward
.keys()
.filter(|n| n.file == file)
.cloned()
.collect();
for node in &to_remove {
if let Some(targets) = self.forward.remove(node) {
for (target, _, _) in &targets {
if let Some(revs) = self.reverse.get_mut(target) {
revs.retain(|n| n != node);
}
}
}
}
let rev_remove: Vec<NodeId> = self
.reverse
.keys()
.filter(|n| n.file == file)
.cloned()
.collect();
for node in &rev_remove {
let _ = self.reverse.remove(node);
}
}
pub fn outgoing(&self, root: &NodeId, depth: u32) -> (Vec<NodeId>, Vec<Edge>) {
self.bfs(root, depth, false)
}
pub fn incoming(&self, root: &NodeId, depth: u32) -> (Vec<NodeId>, Vec<Edge>) {
self.bfs(root, depth, true)
}
fn bfs(&self, root: &NodeId, depth: u32, reverse: bool) -> (Vec<NodeId>, Vec<Edge>) {
let mut visited: HashSet<NodeId> = HashSet::new();
let mut queue: VecDeque<(NodeId, u32)> = VecDeque::new();
let mut result_nodes: Vec<NodeId> = Vec::new();
let mut result_edges: Vec<Edge> = Vec::new();
queue.push_back((root.clone(), 0));
let _ = visited.insert(root.clone());
while let Some((current, d)) = queue.pop_front() {
result_nodes.push(current.clone());
if d >= depth {
continue;
}
if reverse {
if let Some(froms) = self.reverse.get(¤t) {
for from in froms {
if visited.insert(from.clone()) {
result_edges.push(Edge {
from: from.clone(),
to: current.clone(),
kind: EdgeKind::References,
line: 0,
});
queue.push_back((from.clone(), d + 1));
}
}
}
} else if let Some(targets) = self.forward.get(¤t) {
for (to, kind, line) in targets {
if visited.insert(to.clone()) {
result_edges.push(Edge {
from: current.clone(),
to: to.clone(),
kind: *kind,
line: *line,
});
queue.push_back((to.clone(), d + 1));
}
}
}
}
(result_nodes, result_edges)
}
#[must_use]
pub fn edge_count(&self) -> usize {
self.forward.values().map(Vec::len).sum()
}
}