1use petgraph::stable_graph::{EdgeIndex, NodeIndex, StableGraph};
22use petgraph::Directed;
23use serde::{Deserialize, Serialize};
24
25#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
27pub struct DagEdge {
28 pub label: Option<String>,
30}
31
32#[derive(Debug, Clone)]
45pub struct StableDag<N> {
46 inner: StableGraph<N, DagEdge, Directed>,
47}
48
49impl<N: Serialize> Serialize for StableDag<N> {
51 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
52 where
53 S: serde::Serializer,
54 {
55 self.inner.serialize(serializer)
56 }
57}
58
59impl<'de, N: Deserialize<'de>> Deserialize<'de> for StableDag<N> {
61 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
62 where
63 D: serde::Deserializer<'de>,
64 {
65 let inner = StableGraph::<N, DagEdge, Directed>::deserialize(deserializer)?;
66 Ok(Self { inner })
67 }
68}
69
70impl<N> StableDag<N> {
71 pub fn new() -> Self {
73 Self {
74 inner: StableGraph::new(),
75 }
76 }
77
78 pub fn with_capacity(nodes: usize, edges: usize) -> Self {
80 Self {
81 inner: StableGraph::with_capacity(nodes, edges),
82 }
83 }
84
85 #[inline]
87 pub fn node_count(&self) -> usize {
88 self.inner.node_count()
89 }
90
91 #[inline]
93 pub fn edge_count(&self) -> usize {
94 self.inner.edge_count()
95 }
96
97 pub fn add_node(&mut self, weight: N) -> NodeIndex {
104 self.inner.add_node(weight)
105 }
106
107 pub fn node_weight(&self, idx: NodeIndex) -> Option<&N> {
109 self.inner.node_weight(idx)
110 }
111
112 pub fn node_weight_mut(&mut self, idx: NodeIndex) -> Option<&mut N> {
114 self.inner.node_weight_mut(idx)
115 }
116
117 pub fn remove_node(&mut self, idx: NodeIndex) -> Option<N> {
125 self.inner.remove_node(idx)
126 }
127
128 pub fn contains_node(&self, idx: NodeIndex) -> bool {
130 self.inner.contains_node(idx)
131 }
132
133 pub fn add_edge(&mut self, a: NodeIndex, b: NodeIndex) -> Option<EdgeIndex> {
140 if !self.contains_node(a) || !self.contains_node(b) {
141 return None;
142 }
143 Some(self.inner.add_edge(a, b, DagEdge::default()))
144 }
145
146 pub fn add_edge_with_label(
148 &mut self,
149 a: NodeIndex,
150 b: NodeIndex,
151 label: impl Into<String>,
152 ) -> Option<EdgeIndex> {
153 if !self.contains_node(a) || !self.contains_node(b) {
154 return None;
155 }
156 Some(self.inner.add_edge(
157 a,
158 b,
159 DagEdge {
160 label: Some(label.into()),
161 },
162 ))
163 }
164
165 pub fn has_edge(&self, a: NodeIndex, b: NodeIndex) -> bool {
167 self.inner.find_edge(a, b).is_some()
168 }
169
170 pub fn remove_edge(&mut self, a: NodeIndex, b: NodeIndex) -> bool {
173 if let Some(edge) = self.inner.find_edge(a, b) {
174 self.inner.remove_edge(edge);
175 true
176 } else {
177 false
178 }
179 }
180
181 pub fn neighbors_directed(
190 &self,
191 idx: NodeIndex,
192 direction: petgraph::Direction,
193 ) -> impl Iterator<Item = NodeIndex> + '_ {
194 self.inner.neighbors_directed(idx, direction)
195 }
196
197 pub fn incoming_neighbors(&self, idx: NodeIndex) -> impl Iterator<Item = NodeIndex> + '_ {
199 self.neighbors_directed(idx, petgraph::Direction::Incoming)
200 }
201
202 pub fn outgoing_neighbors(&self, idx: NodeIndex) -> impl Iterator<Item = NodeIndex> + '_ {
204 self.neighbors_directed(idx, petgraph::Direction::Outgoing)
205 }
206}
207
208impl<N> Default for StableDag<N> {
209 fn default() -> Self {
210 Self::new()
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn test_stable_graph_new_creates_empty_graph() {
220 let graph: StableDag<String> = StableDag::new();
221 assert_eq!(graph.node_count(), 0);
222 assert_eq!(graph.edge_count(), 0);
223 }
224
225 #[test]
226 fn test_stable_graph_with_capacity() {
227 let graph: StableDag<String> = StableDag::with_capacity(10, 20);
228 assert_eq!(graph.node_count(), 0);
229 }
231
232 #[test]
233 fn test_stable_graph_default_is_empty() {
234 let graph: StableDag<String> = StableDag::default();
235 assert_eq!(graph.node_count(), 0);
236 assert_eq!(graph.edge_count(), 0);
237 }
238
239 #[test]
244 fn test_add_node_returns_stable_index() {
245 let mut graph: StableDag<String> = StableDag::new();
246
247 let idx1 = graph.add_node("msg-001".to_string());
248 let idx2 = graph.add_node("msg-002".to_string());
249
250 assert_eq!(idx1.index(), 0);
251 assert_eq!(idx2.index(), 1);
252 assert_eq!(graph.node_count(), 2);
253 }
254
255 #[test]
256 fn test_add_node_increments_count() {
257 let mut graph: StableDag<String> = StableDag::new();
258
259 assert_eq!(graph.node_count(), 0);
260 graph.add_node("a".to_string());
261 assert_eq!(graph.node_count(), 1);
262 graph.add_node("b".to_string());
263 assert_eq!(graph.node_count(), 2);
264 }
265
266 #[test]
267 fn test_node_weight_retrieves_value() {
268 let mut graph: StableDag<String> = StableDag::new();
269 let idx = graph.add_node("test-value".to_string());
270
271 assert_eq!(graph.node_weight(idx), Some(&"test-value".to_string()));
272 }
273
274 #[test]
275 fn test_node_weight_mut_allows_modification() {
276 let mut graph: StableDag<String> = StableDag::new();
277 let idx = graph.add_node("original".to_string());
278
279 if let Some(weight) = graph.node_weight_mut(idx) {
280 *weight = "modified".to_string();
281 }
282
283 assert_eq!(graph.node_weight(idx), Some(&"modified".to_string()));
284 }
285
286 #[test]
291 fn test_remove_node_preserves_other_indices() {
292 let mut graph: StableDag<String> = StableDag::new();
293
294 let idx0 = graph.add_node("msg-001".to_string());
295 let idx1 = graph.add_node("msg-002".to_string());
296 let idx2 = graph.add_node("msg-003".to_string());
297
298 graph.remove_node(idx1);
300
301 assert_eq!(graph.node_weight(idx0), Some(&"msg-001".to_string()));
303 assert_eq!(graph.node_weight(idx1), None); assert_eq!(graph.node_weight(idx2), Some(&"msg-003".to_string()));
305
306 assert_eq!(idx0.index(), 0);
308 assert_eq!(idx2.index(), 2); }
310
311 #[test]
312 fn test_remove_node_decrements_count() {
313 let mut graph: StableDag<String> = StableDag::new();
314
315 let idx = graph.add_node("test".to_string());
316 assert_eq!(graph.node_count(), 1);
317
318 graph.remove_node(idx);
319 assert_eq!(graph.node_count(), 0);
320 }
321
322 #[test]
323 fn test_remove_node_returns_weight() {
324 let mut graph: StableDag<String> = StableDag::new();
325 let idx = graph.add_node("value".to_string());
326
327 let removed = graph.remove_node(idx);
328 assert_eq!(removed, Some("value".to_string()));
329 }
330
331 #[test]
332 fn test_remove_node_missing_returns_none() {
333 let mut graph: StableDag<String> = StableDag::new();
334 let idx = graph.add_node("value".to_string());
335 graph.remove_node(idx);
336
337 let removed = graph.remove_node(idx);
339 assert_eq!(removed, None);
340 }
341
342 #[test]
343 fn test_contains_node_after_removal() {
344 let mut graph: StableDag<String> = StableDag::new();
345 let idx = graph.add_node("test".to_string());
346
347 assert!(graph.contains_node(idx));
348 graph.remove_node(idx);
349 assert!(!graph.contains_node(idx));
350 }
351
352 #[test]
357 fn test_add_edge_creates_directed_edge() {
358 let mut graph: StableDag<String> = StableDag::new();
359
360 let a = graph.add_node("a".to_string());
361 let b = graph.add_node("b".to_string());
362
363 let edge = graph.add_edge(a, b);
364 assert!(edge.is_some());
365 assert_eq!(graph.edge_count(), 1);
366 }
367
368 #[test]
369 fn test_add_edge_invalid_node_returns_none() {
370 use petgraph::stable_graph::NodeIndex;
371
372 let mut graph: StableDag<String> = StableDag::new();
373
374 let a = graph.add_node("a".to_string());
375 let invalid = NodeIndex::new(999);
376
377 let edge = graph.add_edge(a, invalid);
378 assert!(edge.is_none());
379 assert_eq!(graph.edge_count(), 0);
380 }
381
382 #[test]
383 fn test_has_edge_checks_existence() {
384 let mut graph: StableDag<String> = StableDag::new();
385
386 let a = graph.add_node("a".to_string());
387 let b = graph.add_node("b".to_string());
388 let c = graph.add_node("c".to_string());
389
390 graph.add_edge(a, b);
391
392 assert!(graph.has_edge(a, b));
393 assert!(!graph.has_edge(b, a)); assert!(!graph.has_edge(a, c)); }
396
397 #[test]
398 fn test_remove_edge_works() {
399 let mut graph: StableDag<String> = StableDag::new();
400
401 let a = graph.add_node("a".to_string());
402 let b = graph.add_node("b".to_string());
403
404 graph.add_edge(a, b);
405 assert!(graph.has_edge(a, b));
406 assert_eq!(graph.edge_count(), 1);
407
408 let removed = graph.remove_edge(a, b);
409 assert!(removed);
410 assert!(!graph.has_edge(a, b));
411 assert_eq!(graph.edge_count(), 0);
412 }
413
414 #[test]
415 fn test_remove_edge_nonexistent_returns_false() {
416 let mut graph: StableDag<String> = StableDag::new();
417
418 let a = graph.add_node("a".to_string());
419 let b = graph.add_node("b".to_string());
420
421 let removed = graph.remove_edge(a, b);
422 assert!(!removed);
423 }
424}