grex_core/pack/validate/
cycle.rs1use std::collections::HashSet;
17
18use super::{GraphValidator, PackValidationError};
19use crate::tree::{EdgeKind, PackGraph};
20
21pub struct CycleValidator;
23
24impl GraphValidator for CycleValidator {
25 fn name(&self) -> &'static str {
26 "graph_cycle"
27 }
28
29 fn check(&self, graph: &PackGraph) -> Vec<PackValidationError> {
30 let mut errs = Vec::new();
31 let mut visited: HashSet<usize> = HashSet::new();
32 for start in 0..graph.nodes().len() {
33 if visited.contains(&start) {
34 continue;
35 }
36 let mut on_stack = Vec::new();
37 detect_from(graph, start, &mut visited, &mut on_stack, &mut errs);
38 }
39 errs
40 }
41}
42
43fn detect_from(
46 graph: &PackGraph,
47 id: usize,
48 visited: &mut HashSet<usize>,
49 on_stack: &mut Vec<usize>,
50 errs: &mut Vec<PackValidationError>,
51) {
52 if on_stack.contains(&id) {
53 errs.push(build_cycle_error(graph, on_stack, id));
54 return;
55 }
56 if !visited.insert(id) {
57 return;
58 }
59 on_stack.push(id);
60 for edge in graph.edges() {
61 if edge.from == id && edge.kind == EdgeKind::Child {
62 detect_from(graph, edge.to, visited, on_stack, errs);
63 }
64 }
65 on_stack.pop();
66}
67
68fn build_cycle_error(
71 graph: &PackGraph,
72 on_stack: &[usize],
73 back_edge_target: usize,
74) -> PackValidationError {
75 let mut chain: Vec<String> =
76 on_stack.iter().filter_map(|id| graph.node(*id).map(|n| n.name.clone())).collect();
77 if let Some(n) = graph.node(back_edge_target) {
78 chain.push(n.name.clone());
79 }
80 PackValidationError::GraphCycle { chain }
81}