batuta/tui/graph/
graph_core.rs1use std::collections::HashMap;
6
7use super::types::{Edge, Node, MAX_TUI_NODES};
8
9#[derive(Debug, Clone)]
15pub struct Graph<N, E> {
16 pub(crate) nodes: HashMap<String, Node<N>>,
18 pub(crate) edges: Vec<Edge<E>>,
20 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 #[must_use]
33 pub fn new() -> Self {
34 Self { nodes: HashMap::new(), edges: Vec::new(), adjacency: HashMap::new() }
35 }
36
37 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 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 #[must_use]
52 pub fn node_count(&self) -> usize {
53 self.nodes.len()
54 }
55
56 #[must_use]
58 pub fn edge_count(&self) -> usize {
59 self.edges.len()
60 }
61
62 #[must_use]
64 pub fn get_node(&self, id: &str) -> Option<&Node<N>> {
65 self.nodes.get(id)
66 }
67
68 pub fn get_node_mut(&mut self, id: &str) -> Option<&mut Node<N>> {
70 self.nodes.get_mut(id)
71 }
72
73 pub fn nodes(&self) -> impl Iterator<Item = &Node<N>> {
75 self.nodes.values()
76 }
77
78 pub fn nodes_mut(&mut self) -> impl Iterator<Item = &mut Node<N>> {
80 self.nodes.values_mut()
81 }
82
83 pub fn edges(&self) -> impl Iterator<Item = &Edge<E>> {
85 self.edges.iter()
86 }
87
88 #[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 #[must_use]
96 pub fn exceeds_tui_limit(&self) -> bool {
97 self.nodes.len() > MAX_TUI_NODES
98 }
99
100 #[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 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}