Skip to main content

batuta/tui/graph/
graph_core.rs

1//! Core Graph data structure
2//!
3//! Contains the `Graph` struct and its core methods for node/edge management.
4
5use std::collections::HashMap;
6
7use super::types::{Edge, Node, MAX_TUI_NODES};
8
9// ============================================================================
10// GRAPH-002: Graph Data Structure
11// ============================================================================
12
13/// Graph for TUI visualization
14#[derive(Debug, Clone)]
15pub struct Graph<N, E> {
16    /// Nodes indexed by ID
17    pub(crate) nodes: HashMap<String, Node<N>>,
18    /// Edges
19    pub(crate) edges: Vec<Edge<E>>,
20    /// Adjacency list for fast neighbor lookup
21    adjacency: HashMap<String, Vec<String>>,
22}
23
24impl<N, E> Default for Graph<N, E> {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl<N, E> Graph<N, E> {
31    /// Create empty graph
32    #[must_use]
33    pub fn new() -> Self {
34        Self { nodes: HashMap::new(), edges: Vec::new(), adjacency: HashMap::new() }
35    }
36
37    /// Add a node
38    pub fn add_node(&mut self, node: Node<N>) {
39        let id = node.id.clone();
40        self.nodes.insert(id.clone(), node);
41        self.adjacency.entry(id).or_default();
42    }
43
44    /// Add an edge
45    pub fn add_edge(&mut self, edge: Edge<E>) {
46        self.adjacency.entry(edge.from.clone()).or_default().push(edge.to.clone());
47        self.edges.push(edge);
48    }
49
50    /// Get node count
51    #[must_use]
52    pub fn node_count(&self) -> usize {
53        self.nodes.len()
54    }
55
56    /// Get edge count
57    #[must_use]
58    pub fn edge_count(&self) -> usize {
59        self.edges.len()
60    }
61
62    /// Get node by ID
63    #[must_use]
64    pub fn get_node(&self, id: &str) -> Option<&Node<N>> {
65        self.nodes.get(id)
66    }
67
68    /// Get mutable node by ID
69    pub fn get_node_mut(&mut self, id: &str) -> Option<&mut Node<N>> {
70        self.nodes.get_mut(id)
71    }
72
73    /// Iterate over nodes
74    pub fn nodes(&self) -> impl Iterator<Item = &Node<N>> {
75        self.nodes.values()
76    }
77
78    /// Iterate over nodes mutably
79    pub fn nodes_mut(&mut self) -> impl Iterator<Item = &mut Node<N>> {
80        self.nodes.values_mut()
81    }
82
83    /// Iterate over edges
84    pub fn edges(&self) -> impl Iterator<Item = &Edge<E>> {
85        self.edges.iter()
86    }
87
88    /// Get neighbors of a node
89    #[must_use]
90    pub fn neighbors(&self, id: &str) -> Vec<&str> {
91        self.adjacency.get(id).map(|v| v.iter().map(String::as_str).collect()).unwrap_or_default()
92    }
93
94    /// Check if graph exceeds TUI limit (Muri prevention)
95    #[must_use]
96    pub fn exceeds_tui_limit(&self) -> bool {
97        self.nodes.len() > MAX_TUI_NODES
98    }
99
100    /// Get top N nodes by importance (Mieruka)
101    #[must_use]
102    pub fn top_nodes_by_importance(&self, n: usize) -> Vec<&Node<N>> {
103        let mut nodes: Vec<_> = self.nodes.values().collect();
104        nodes.sort_by(|a, b| {
105            b.importance.partial_cmp(&a.importance).unwrap_or(std::cmp::Ordering::Equal)
106        });
107        nodes.into_iter().take(n).collect()
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_graph_new() {
117        let graph: Graph<(), ()> = Graph::new();
118        assert_eq!(graph.node_count(), 0);
119        assert_eq!(graph.edge_count(), 0);
120    }
121
122    #[test]
123    fn test_graph_default() {
124        let graph: Graph<i32, &str> = Graph::default();
125        assert_eq!(graph.node_count(), 0);
126    }
127
128    #[test]
129    fn test_graph_add_node() {
130        let mut graph: Graph<i32, ()> = Graph::new();
131        graph.add_node(Node::new("A", 42));
132        assert_eq!(graph.node_count(), 1);
133        assert!(graph.get_node("A").is_some());
134    }
135
136    #[test]
137    fn test_graph_add_edge() {
138        let mut graph: Graph<(), &str> = Graph::new();
139        graph.add_node(Node::new("A", ()));
140        graph.add_node(Node::new("B", ()));
141        graph.add_edge(Edge::new("A", "B", "connects"));
142        assert_eq!(graph.edge_count(), 1);
143    }
144
145    #[test]
146    fn test_graph_get_node_not_found() {
147        let graph: Graph<(), ()> = Graph::new();
148        assert!(graph.get_node("X").is_none());
149    }
150
151    #[test]
152    fn test_graph_get_node_mut() {
153        let mut graph: Graph<i32, ()> = Graph::new();
154        graph.add_node(Node::new("A", 10));
155        if let Some(node) = graph.get_node_mut("A") {
156            node.importance = 5.0;
157        }
158        assert_eq!(graph.get_node("A").expect("unexpected failure").importance, 5.0);
159    }
160
161    #[test]
162    fn test_graph_nodes_iterator() {
163        let mut graph: Graph<i32, ()> = Graph::new();
164        graph.add_node(Node::new("A", 1));
165        graph.add_node(Node::new("B", 2));
166        let count = graph.nodes().count();
167        assert_eq!(count, 2);
168    }
169
170    #[test]
171    fn test_graph_nodes_mut_iterator() {
172        let mut graph: Graph<i32, ()> = Graph::new();
173        graph.add_node(Node::new("A", 1));
174        for node in graph.nodes_mut() {
175            node.importance = 0.5;
176        }
177        assert_eq!(graph.get_node("A").expect("unexpected failure").importance, 0.5);
178    }
179
180    #[test]
181    fn test_graph_edges_iterator() {
182        let mut graph: Graph<(), i32> = Graph::new();
183        graph.add_node(Node::new("A", ()));
184        graph.add_node(Node::new("B", ()));
185        graph.add_edge(Edge::new("A", "B", 100));
186        let edges: Vec<_> = graph.edges().collect();
187        assert_eq!(edges.len(), 1);
188        assert_eq!(edges[0].data, 100);
189    }
190
191    #[test]
192    fn test_graph_neighbors() {
193        let mut graph: Graph<(), ()> = Graph::new();
194        graph.add_node(Node::new("A", ()));
195        graph.add_node(Node::new("B", ()));
196        graph.add_node(Node::new("C", ()));
197        graph.add_edge(Edge::new("A", "B", ()));
198        graph.add_edge(Edge::new("A", "C", ()));
199        let neighbors = graph.neighbors("A");
200        assert_eq!(neighbors.len(), 2);
201    }
202
203    #[test]
204    fn test_graph_neighbors_empty() {
205        let graph: Graph<(), ()> = Graph::new();
206        let neighbors = graph.neighbors("X");
207        assert!(neighbors.is_empty());
208    }
209
210    #[test]
211    fn test_graph_exceeds_tui_limit() {
212        let graph: Graph<(), ()> = Graph::new();
213        assert!(!graph.exceeds_tui_limit());
214    }
215
216    #[test]
217    fn test_graph_top_nodes_by_importance() {
218        let mut graph: Graph<(), ()> = Graph::new();
219        for i in 0..5 {
220            let mut node = Node::new(format!("n{}", i), ());
221            node.importance = i as f32;
222            graph.add_node(node);
223        }
224        let top = graph.top_nodes_by_importance(3);
225        assert_eq!(top.len(), 3);
226        // Highest importance first
227        assert!(top[0].importance >= top[1].importance);
228        assert!(top[1].importance >= top[2].importance);
229    }
230
231    #[test]
232    fn test_graph_top_nodes_fewer_than_n() {
233        let mut graph: Graph<(), ()> = Graph::new();
234        graph.add_node(Node::new("A", ()));
235        let top = graph.top_nodes_by_importance(10);
236        assert_eq!(top.len(), 1);
237    }
238}