1use std::collections::{BTreeMap, BTreeSet, BinaryHeap};
11use std::hash::Hash;
12
13use crate::plan::edges::{DepEdge, DepSource, NodeId};
14
15#[derive(Debug, Clone)]
17pub struct Graph<N> {
18 nodes: BTreeSet<N>,
19 edges: BTreeMap<N, BTreeSet<N>>,
21 edge_sources: BTreeMap<(N, N), DepSource>,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct Cycle<N> {
29 pub nodes: Vec<N>,
31}
32
33impl<N> Default for Graph<N>
34where
35 N: Hash + Eq + Clone + Ord,
36{
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl<N> Graph<N>
43where
44 N: Hash + Eq + Clone + Ord,
45{
46 pub const fn new() -> Self {
48 Self {
49 nodes: BTreeSet::new(),
50 edges: BTreeMap::new(),
51 edge_sources: BTreeMap::new(),
52 }
53 }
54
55 pub fn add_node(&mut self, n: N) {
57 self.nodes.insert(n);
58 }
59
60 pub fn add_edge(&mut self, from: N, to: N) {
67 self.add_edge_internal(from, to);
68 }
69
70 fn add_edge_internal(&mut self, from: N, to: N) {
72 self.nodes.insert(from.clone());
73 self.nodes.insert(to.clone());
74 self.edges.entry(from).or_default().insert(to);
75 }
76
77 pub fn remove_edge(&mut self, from: &N, to: &N) {
85 if let Some(set) = self.edges.get_mut(from) {
86 set.remove(to);
87 if set.is_empty() {
88 self.edges.remove(from);
89 }
90 }
91 }
92
93 pub fn node_count(&self) -> usize {
95 self.nodes.len()
96 }
97
98 pub fn nodes(&self) -> impl Iterator<Item = &N> {
100 self.nodes.iter()
101 }
102
103 pub fn dependencies_of<'a>(&'a self, n: &N) -> impl Iterator<Item = &'a N> + 'a + use<'a, N> {
105 self.edges.get(n).into_iter().flat_map(BTreeSet::iter)
106 }
107
108 pub fn topological_sort(&self) -> Result<Vec<N>, Cycle<N>> {
113 let mut in_degree: BTreeMap<N, usize> =
116 self.nodes.iter().map(|n| (n.clone(), 0_usize)).collect();
117 let mut dependents: BTreeMap<N, Vec<N>> = BTreeMap::new();
118
119 for (from, deps) in &self.edges {
120 *in_degree.entry(from.clone()).or_insert(0) += deps.len();
125 for to in deps {
126 dependents.entry(to.clone()).or_default().push(from.clone());
127 }
128 }
129
130 let mut ready: BinaryHeap<std::cmp::Reverse<N>> = in_degree
132 .iter()
133 .filter(|(_, d)| **d == 0)
134 .map(|(n, _)| std::cmp::Reverse(n.clone()))
135 .collect();
136
137 let mut out = Vec::with_capacity(self.nodes.len());
138
139 while let Some(std::cmp::Reverse(n)) = ready.pop() {
140 out.push(n.clone());
141 if let Some(parents) = dependents.get(&n) {
142 for p in parents {
143 if let Some(d) = in_degree.get_mut(p) {
144 *d -= 1;
145 if *d == 0 {
146 ready.push(std::cmp::Reverse(p.clone()));
147 }
148 }
149 }
150 }
151 }
152
153 if out.len() == self.nodes.len() {
154 Ok(out)
155 } else {
156 let cycle_nodes: Vec<N> = in_degree
158 .into_iter()
159 .filter_map(|(n, d)| (d > 0).then_some(n))
160 .collect();
161 Err(Cycle { nodes: cycle_nodes })
162 }
163 }
164
165 pub fn reverse_topological_sort(&self) -> Result<Vec<N>, Cycle<N>> {
168 let mut v = self.topological_sort()?;
169 v.reverse();
170 Ok(v)
171 }
172
173 pub fn edges(&self) -> impl Iterator<Item = (&N, &N)> {
175 self.edges
176 .iter()
177 .flat_map(|(from, targets)| targets.iter().map(move |to| (from, to)))
178 }
179}
180
181impl Graph<NodeId> {
182 pub fn add_dep_edge(&mut self, from: NodeId, to: NodeId, source: DepSource) {
192 self.add_edge_internal(from.clone(), to.clone());
193 self.edge_sources.insert((from, to), source);
194 }
195
196 pub fn remove_dep_edge(&mut self, from: &NodeId, to: &NodeId) {
203 self.remove_edge(from, to);
204 self.edge_sources.remove(&(from.clone(), to.clone()));
205 }
206
207 pub fn dep_edges(&self) -> impl Iterator<Item = DepEdge> + '_ {
213 self.edges().map(|(from, to)| {
214 let source = self
215 .edge_sources
216 .get(&(from.clone(), to.clone()))
217 .copied()
218 .unwrap_or(DepSource::Structural);
219 DepEdge {
220 from: from.clone(),
221 to: to.clone(),
222 source,
223 }
224 })
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 #[test]
233 fn empty_graph_sorts_to_empty() {
234 let g: Graph<i32> = Graph::new();
235 assert_eq!(g.topological_sort().unwrap(), Vec::<i32>::new());
236 }
237
238 #[test]
239 fn single_node_sorts_to_self() {
240 let mut g: Graph<i32> = Graph::new();
241 g.add_node(7);
242 assert_eq!(g.topological_sort().unwrap(), vec![7]);
243 }
244
245 #[test]
246 fn linear_chain_sorts_in_dependency_order() {
247 let mut g: Graph<&'static str> = Graph::new();
250 g.add_edge("a", "b");
251 g.add_edge("b", "c");
252 assert_eq!(g.topological_sort().unwrap(), vec!["c", "b", "a"]);
253 }
254
255 #[test]
256 fn diamond_sorts_with_deterministic_tie_break() {
257 let mut g: Graph<&'static str> = Graph::new();
260 g.add_edge("a", "b");
261 g.add_edge("a", "c");
262 g.add_edge("b", "d");
263 g.add_edge("c", "d");
264 assert_eq!(g.topological_sort().unwrap(), vec!["d", "b", "c", "a"]);
265 }
266
267 #[test]
268 fn disconnected_components_sort_deterministically() {
269 let mut g: Graph<&'static str> = Graph::new();
270 g.add_edge("a", "b");
271 g.add_edge("x", "y");
272 assert_eq!(g.topological_sort().unwrap(), vec!["b", "a", "y", "x"]);
275 }
276
277 #[test]
278 fn isolated_nodes_appear_in_order() {
279 let mut g: Graph<i32> = Graph::new();
280 g.add_node(3);
281 g.add_node(1);
282 g.add_node(2);
283 assert_eq!(g.topological_sort().unwrap(), vec![1, 2, 3]);
284 }
285
286 #[test]
287 fn cycle_of_two_detected() {
288 let mut g: Graph<&'static str> = Graph::new();
289 g.add_edge("a", "b");
290 g.add_edge("b", "a");
291 let err = g.topological_sort().unwrap_err();
292 assert_eq!(err.nodes, vec!["a", "b"]);
293 }
294
295 #[test]
296 fn cycle_of_three_detected() {
297 let mut g: Graph<&'static str> = Graph::new();
298 g.add_edge("a", "b");
299 g.add_edge("b", "c");
300 g.add_edge("c", "a");
301 let err = g.topological_sort().unwrap_err();
302 assert_eq!(err.nodes, vec!["a", "b", "c"]);
303 }
304
305 #[test]
306 fn cycle_does_not_block_acyclic_part() {
307 let mut g: Graph<&'static str> = Graph::new();
310 g.add_edge("x", "y");
311 g.add_edge("a", "b");
312 g.add_edge("b", "a");
313 let err = g.topological_sort().unwrap_err();
314 assert_eq!(err.nodes, vec!["a", "b"]);
315 }
316
317 #[test]
318 fn reverse_sort_inverts_order() {
319 let mut g: Graph<&'static str> = Graph::new();
320 g.add_edge("a", "b");
321 g.add_edge("b", "c");
322 assert_eq!(g.reverse_topological_sort().unwrap(), vec!["a", "b", "c"]);
323 }
324
325 #[test]
326 fn remove_edge_breaks_cycle() {
327 let mut g: Graph<&'static str> = Graph::new();
328 g.add_edge("a", "b");
329 g.add_edge("b", "a");
330 assert!(g.topological_sort().is_err());
331 g.remove_edge(&"b", &"a");
332 assert_eq!(g.topological_sort().unwrap(), vec!["b", "a"]);
333 }
334
335 #[test]
336 fn deterministic_under_insertion_order_changes() {
337 let mut g1: Graph<&'static str> = Graph::new();
338 g1.add_edge("a", "b");
339 g1.add_edge("a", "c");
340 g1.add_edge("b", "d");
341 g1.add_edge("c", "d");
342
343 let mut g2: Graph<&'static str> = Graph::new();
344 g2.add_edge("c", "d");
345 g2.add_edge("a", "c");
346 g2.add_edge("b", "d");
347 g2.add_edge("a", "b");
348
349 assert_eq!(g1.topological_sort(), g2.topological_sort());
350 }
351
352 #[test]
353 fn dependencies_of_returns_in_ord_order() {
354 let mut g: Graph<&'static str> = Graph::new();
355 g.add_edge("a", "z");
356 g.add_edge("a", "b");
357 g.add_edge("a", "m");
358 let deps: Vec<&&str> = g.dependencies_of(&"a").collect();
359 assert_eq!(deps, vec![&"b", &"m", &"z"]);
360 }
361
362 #[test]
363 fn edges_yields_correct_pair_for_single_edge() {
364 use crate::identifier::Identifier;
368 let mut g: Graph<NodeId> = Graph::new();
369 let schema_a = NodeId::Schema(Identifier::from_unquoted("a").unwrap());
370 let schema_b = NodeId::Schema(Identifier::from_unquoted("b").unwrap());
371 g.add_edge(schema_a.clone(), schema_b.clone());
372 let pairs: Vec<(&NodeId, &NodeId)> = g.edges().collect();
373 assert_eq!(pairs, vec![(&schema_a, &schema_b)]);
374 }
375}