devsper_graph/
validator.rs1use devsper_core::{GraphMutation, NodeId};
2use petgraph::algo::is_cyclic_directed;
3use petgraph::graph::{DiGraph, NodeIndex};
4use std::collections::HashMap;
5
6pub struct MutationValidator;
9
10impl MutationValidator {
11 pub fn new() -> Self {
12 Self
13 }
14
15 pub fn validate(
17 &self,
18 graph: &DiGraph<NodeId, ()>,
19 index_map: &HashMap<NodeId, NodeIndex>,
20 mutation: &GraphMutation,
21 ) -> Result<(), String> {
22 match mutation {
23 GraphMutation::AddEdge { from, to } => {
24 self.validate_add_edge(graph, index_map, from, to)
25 }
26 GraphMutation::InjectBefore { .. } => Ok(()),
28 GraphMutation::SplitNode { .. } => Ok(()),
30 _ => Ok(()),
32 }
33 }
34
35 fn validate_add_edge(
36 &self,
37 graph: &DiGraph<NodeId, ()>,
38 index_map: &HashMap<NodeId, NodeIndex>,
39 from: &NodeId,
40 to: &NodeId,
41 ) -> Result<(), String> {
42 let from_idx = index_map
43 .get(from)
44 .copied()
45 .ok_or_else(|| format!("Node not found: {from}"))?;
46 let to_idx = index_map
47 .get(to)
48 .copied()
49 .ok_or_else(|| format!("Node not found: {to}"))?;
50
51 let mut test_graph = graph.clone();
53 test_graph.add_edge(from_idx, to_idx, ());
54
55 if is_cyclic_directed(&test_graph) {
56 Err(format!("Edge {from} → {to} would create a cycle"))
57 } else {
58 Ok(())
59 }
60 }
61}
62
63impl Default for MutationValidator {
64 fn default() -> Self {
65 Self::new()
66 }
67}