1use std::collections::{HashMap, HashSet};
21use std::fmt::{Debug, Display};
22use std::hash::Hash;
23
24mod err;
25pub use err::*;
26use itertools::Itertools;
27
28pub trait TCNode<K> {
34 fn get_key(&self) -> K;
36
37 fn add_edge_to(&mut self, k: K);
39
40 fn out_edges(&self) -> Box<dyn Iterator<Item = &K> + '_>;
42
43 fn has_edge_to(&self, k: &K) -> bool;
46
47 fn reset_edges(&mut self);
49
50 fn direct_edges(&self) -> Box<dyn Iterator<Item = &K> + '_> {
52 self.out_edges()
53 }
54}
55
56pub fn compute_tc<K, V>(nodes: &mut HashMap<K, V>, enforce_dag: bool) -> Result<(), K>
62where
63 K: Clone + Eq + Hash + Debug + Display,
64 V: TCNode<K>,
65{
66 compute_tc_internal::<K, V>(nodes);
67 if enforce_dag {
68 return enforce_dag_from_tc(nodes);
69 }
70 Ok(())
71}
72
73fn compute_tc_internal<K, V>(nodes: &mut HashMap<K, V>)
78where
79 K: Clone + Eq + Hash,
80 V: TCNode<K>,
81{
82 let mut ancestors: HashMap<K, HashSet<K>> = HashMap::new();
87 for node in nodes.values() {
88 let this_node_ancestors: &mut HashSet<K> = ancestors.entry(node.get_key()).or_default();
89 add_ancestors_to_set(node, nodes, this_node_ancestors);
90 }
91 for node in nodes.values_mut() {
92 node.reset_edges();
93 #[expect(
94 clippy::expect_used,
95 reason = "All nodes in `ancestors` came from `nodes`"
96 )]
97 for ancestor_uid in ancestors
98 .get(&node.get_key())
99 .expect("shouldn't have added any new values to the `nodes` map")
100 {
101 node.add_edge_to(ancestor_uid.clone());
102 }
103 }
104}
105
106pub fn enforce_tc_and_dag<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
111where
112 K: Clone + Eq + Hash + Debug + Display,
113 V: TCNode<K>,
114{
115 let res = enforce_tc(entities);
116 if res.is_ok() {
117 return enforce_dag_from_tc(entities);
118 }
119 res
120}
121
122fn enforce_tc<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
127where
128 K: Clone + Eq + Hash + Debug + Display,
129 V: TCNode<K>,
130{
131 for entity in entities.values() {
132 for parent_uid in entity.out_edges() {
133 if let Some(parent) = entities.get(parent_uid) {
135 for grandparent in parent.out_edges() {
136 if !entity.has_edge_to(grandparent) {
137 return Err(TcError::missing_tc_edge(
138 entity.get_key(),
139 parent_uid.clone(),
140 grandparent.clone(),
141 ));
142 }
143 }
144 }
145 }
146 }
147 Ok(())
148}
149
150fn add_ancestors_to_set<K, V>(node: &V, hierarchy: &HashMap<K, V>, ancestors: &mut HashSet<K>)
154where
155 K: Clone + Eq + Hash,
156 V: TCNode<K>,
157{
158 for parent_uid in node.direct_edges() {
159 if ancestors.insert(parent_uid.clone()) {
160 if let Some(ancestor) = hierarchy.get(parent_uid) {
163 add_ancestors_to_set(ancestor, hierarchy, ancestors);
164 }
165 }
166 }
167}
168
169fn enforce_dag_from_tc<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
175where
176 K: Clone + Eq + Hash + Debug + Display,
177 V: TCNode<K>,
178{
179 for entity in entities.values() {
180 let key = entity.get_key();
181 if entity.out_edges().contains(&key) {
182 return Err(TcError::has_cycle(key));
183 }
184 }
185 Ok(())
186}
187
188#[cfg(test)]
189#[expect(clippy::panic, clippy::indexing_slicing, reason = "test code")]
190mod tests {
191 use crate::ast::{Entity, EntityUID};
192
193 use super::*;
194
195 #[test]
196 fn basic() {
197 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
199 a.add_parent(EntityUID::with_eid("B"));
200 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
201 b.add_parent(EntityUID::with_eid("C"));
202 let c = Entity::with_uid(EntityUID::with_eid("C"));
203 let mut entities = HashMap::from([
204 (a.uid().clone(), a),
205 (b.uid().clone(), b),
206 (c.uid().clone(), c),
207 ]);
208 assert!(enforce_tc(&entities).is_err());
210 compute_tc_internal(&mut entities);
212 let a = &entities[&EntityUID::with_eid("A")];
213 let b = &entities[&EntityUID::with_eid("B")];
214 let c = &entities[&EntityUID::with_eid("C")];
215 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
217 assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
219 assert!(!c.is_descendant_of(&EntityUID::with_eid("A")));
220 assert!(enforce_tc(&entities).is_ok());
222 assert!(enforce_dag_from_tc(&entities).is_ok());
224 }
225
226 #[test]
227 fn reversed() {
228 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
231 a.add_parent(EntityUID::with_eid("B"));
232 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
233 b.add_parent(EntityUID::with_eid("C"));
234 let c = Entity::with_uid(EntityUID::with_eid("C"));
235 let mut entities = HashMap::from([
236 (c.uid().clone(), c),
237 (b.uid().clone(), b),
238 (a.uid().clone(), a),
239 ]);
240 assert!(enforce_tc(&entities).is_err());
242 compute_tc_internal(&mut entities);
244 let a = &entities[&EntityUID::with_eid("A")];
245 let b = &entities[&EntityUID::with_eid("B")];
246 let c = &entities[&EntityUID::with_eid("C")];
247 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
249 assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
251 assert!(!c.is_descendant_of(&EntityUID::with_eid("A")));
252 assert!(enforce_tc(&entities).is_ok());
254 assert!(enforce_dag_from_tc(&entities).is_ok());
256 }
257
258 #[test]
259 fn deeper() {
260 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
262 a.add_parent(EntityUID::with_eid("B"));
263 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
264 b.add_parent(EntityUID::with_eid("C"));
265 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
266 c.add_parent(EntityUID::with_eid("D"));
267 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
268 d.add_parent(EntityUID::with_eid("E"));
269 let e = Entity::with_uid(EntityUID::with_eid("E"));
270 let mut entities = HashMap::from([
271 (a.uid().clone(), a),
272 (b.uid().clone(), b),
273 (c.uid().clone(), c),
274 (d.uid().clone(), d),
275 (e.uid().clone(), e),
276 ]);
277 assert!(enforce_tc(&entities).is_err());
279 compute_tc_internal(&mut entities);
281 let a = &entities[&EntityUID::with_eid("A")];
282 let b = &entities[&EntityUID::with_eid("B")];
283 let c = &entities[&EntityUID::with_eid("C")];
284 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
286 assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
287 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
288 assert!(b.is_descendant_of(&EntityUID::with_eid("D")));
289 assert!(b.is_descendant_of(&EntityUID::with_eid("E")));
290 assert!(c.is_descendant_of(&EntityUID::with_eid("E")));
291 assert!(enforce_tc(&entities).is_ok());
293 assert!(enforce_dag_from_tc(&entities).is_ok());
295 }
296
297 #[test]
298 fn not_alphabetized() {
299 let mut foo = Entity::with_uid(EntityUID::with_eid("foo"));
305 foo.add_parent(EntityUID::with_eid("bar"));
306 let mut bar = Entity::with_uid(EntityUID::with_eid("bar"));
307 bar.add_parent(EntityUID::with_eid("baz"));
308 let mut baz = Entity::with_uid(EntityUID::with_eid("baz"));
309 baz.add_parent(EntityUID::with_eid("ham"));
310 let mut ham = Entity::with_uid(EntityUID::with_eid("ham"));
311 ham.add_parent(EntityUID::with_eid("eggs"));
312 let eggs = Entity::with_uid(EntityUID::with_eid("eggs"));
313 let mut entities = HashMap::from([
314 (ham.uid().clone(), ham),
315 (bar.uid().clone(), bar),
316 (foo.uid().clone(), foo),
317 (eggs.uid().clone(), eggs),
318 (baz.uid().clone(), baz),
319 ]);
320 assert!(enforce_tc(&entities).is_err());
322 compute_tc_internal(&mut entities);
324 let foo = &entities[&EntityUID::with_eid("foo")];
325 let bar = &entities[&EntityUID::with_eid("bar")];
326 let baz = &entities[&EntityUID::with_eid("baz")];
327 assert!(foo.is_descendant_of(&EntityUID::with_eid("baz")));
329 assert!(foo.is_descendant_of(&EntityUID::with_eid("ham")));
330 assert!(foo.is_descendant_of(&EntityUID::with_eid("eggs")));
331 assert!(bar.is_descendant_of(&EntityUID::with_eid("ham")));
332 assert!(bar.is_descendant_of(&EntityUID::with_eid("eggs")));
333 assert!(baz.is_descendant_of(&EntityUID::with_eid("eggs")));
334 assert!(enforce_tc(&entities).is_ok());
336 assert!(enforce_dag_from_tc(&entities).is_ok());
338 }
339
340 #[test]
341 fn multi_parents() {
342 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
349 a.add_parent(EntityUID::with_eid("B"));
350 a.add_parent(EntityUID::with_eid("D"));
351 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
352 b.add_parent(EntityUID::with_eid("C"));
353 let c = Entity::with_uid(EntityUID::with_eid("C"));
354 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
355 d.add_parent(EntityUID::with_eid("E"));
356 let e = Entity::with_uid(EntityUID::with_eid("E"));
357 let mut entities = HashMap::from([
358 (a.uid().clone(), a),
359 (b.uid().clone(), b),
360 (c.uid().clone(), c),
361 (d.uid().clone(), d),
362 (e.uid().clone(), e),
363 ]);
364 assert!(enforce_tc(&entities).is_err());
366 compute_tc_internal(&mut entities);
368 let a = &entities[&EntityUID::with_eid("A")];
369 let b = &entities[&EntityUID::with_eid("B")];
370 let d = &entities[&EntityUID::with_eid("D")];
371 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
373 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
374 assert!(!b.is_descendant_of(&EntityUID::with_eid("D")));
376 assert!(!b.is_descendant_of(&EntityUID::with_eid("E")));
377 assert!(!d.is_descendant_of(&EntityUID::with_eid("B")));
378 assert!(!d.is_descendant_of(&EntityUID::with_eid("C")));
379 assert!(enforce_tc(&entities).is_ok());
381 assert!(enforce_dag_from_tc(&entities).is_ok());
383 }
384
385 #[test]
386 fn dag() {
387 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
394 a.add_parent(EntityUID::with_eid("B"));
395 a.add_parent(EntityUID::with_eid("F"));
396 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
397 b.add_parent(EntityUID::with_eid("C"));
398 b.add_parent(EntityUID::with_eid("D"));
399 let c = Entity::with_uid(EntityUID::with_eid("C"));
400 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
401 d.add_parent(EntityUID::with_eid("E"));
402 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
403 e.add_parent(EntityUID::with_eid("H"));
404 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
405 f.add_parent(EntityUID::with_eid("G"));
406 let mut g = Entity::with_uid(EntityUID::with_eid("G"));
407 g.add_parent(EntityUID::with_eid("E"));
408 let h = Entity::with_uid(EntityUID::with_eid("H"));
409 let mut entities = HashMap::from([
410 (a.uid().clone(), a),
411 (b.uid().clone(), b),
412 (c.uid().clone(), c),
413 (d.uid().clone(), d),
414 (e.uid().clone(), e),
415 (f.uid().clone(), f),
416 (g.uid().clone(), g),
417 (h.uid().clone(), h),
418 ]);
419 assert!(enforce_tc(&entities).is_err());
421 compute_tc_internal(&mut entities);
423 let a = &entities[&EntityUID::with_eid("A")];
424 let b = &entities[&EntityUID::with_eid("B")];
425 let f = &entities[&EntityUID::with_eid("F")];
426 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
428 assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
429 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
430 assert!(a.is_descendant_of(&EntityUID::with_eid("F")));
431 assert!(a.is_descendant_of(&EntityUID::with_eid("G")));
432 assert!(a.is_descendant_of(&EntityUID::with_eid("H")));
433 assert!(b.is_descendant_of(&EntityUID::with_eid("E")));
434 assert!(b.is_descendant_of(&EntityUID::with_eid("H")));
435 assert!(f.is_descendant_of(&EntityUID::with_eid("E")));
436 assert!(f.is_descendant_of(&EntityUID::with_eid("H")));
437 assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
439 assert!(!b.is_descendant_of(&EntityUID::with_eid("G")));
440 assert!(!f.is_descendant_of(&EntityUID::with_eid("C")));
441 assert!(!f.is_descendant_of(&EntityUID::with_eid("D")));
442 assert!(enforce_tc(&entities).is_ok());
444 assert!(enforce_dag_from_tc(&entities).is_ok());
446 }
447
448 #[test]
449 fn already_edges() {
450 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
458 a.add_parent(EntityUID::with_eid("B"));
459 a.add_parent(EntityUID::with_eid("C"));
460 a.add_parent(EntityUID::with_eid("D"));
461 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
462 b.add_parent(EntityUID::with_eid("C"));
463 b.add_parent(EntityUID::with_eid("E"));
464 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
465 c.add_parent(EntityUID::with_eid("E"));
466 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
467 d.add_parent(EntityUID::with_eid("C"));
468 d.add_parent(EntityUID::with_eid("F"));
469 let e = Entity::with_uid(EntityUID::with_eid("E"));
470 let f = Entity::with_uid(EntityUID::with_eid("F"));
471 let mut entities = HashMap::from([
472 (a.uid().clone(), a),
473 (b.uid().clone(), b),
474 (c.uid().clone(), c),
475 (d.uid().clone(), d),
476 (e.uid().clone(), e),
477 (f.uid().clone(), f),
478 ]);
479 assert!(enforce_tc(&entities).is_err());
481 compute_tc_internal(&mut entities);
483 let a = &entities[&EntityUID::with_eid("A")];
484 let b = &entities[&EntityUID::with_eid("B")];
485 let c = &entities[&EntityUID::with_eid("C")];
486 let d = &entities[&EntityUID::with_eid("D")];
487 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
489 assert!(a.is_descendant_of(&EntityUID::with_eid("F")));
490 assert!(d.is_descendant_of(&EntityUID::with_eid("E")));
491 assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
493 assert!(!c.is_descendant_of(&EntityUID::with_eid("F")));
494 assert!(enforce_tc(&entities).is_ok());
496 assert!(enforce_dag_from_tc(&entities).is_ok());
498 }
499
500 #[test]
501 fn disjoint_dag() {
502 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
509 a.add_parent(EntityUID::with_eid("F"));
510 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
511 b.add_parent(EntityUID::with_eid("C"));
512 let c = Entity::with_uid(EntityUID::with_eid("C"));
513 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
514 d.add_parent(EntityUID::with_eid("E"));
515 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
516 e.add_parent(EntityUID::with_eid("H"));
517 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
518 f.add_parent(EntityUID::with_eid("G"));
519 let g = Entity::with_uid(EntityUID::with_eid("G"));
520 let h = Entity::with_uid(EntityUID::with_eid("H"));
521 let mut entities = HashMap::from([
522 (a.uid().clone(), a),
523 (b.uid().clone(), b),
524 (c.uid().clone(), c),
525 (d.uid().clone(), d),
526 (e.uid().clone(), e),
527 (f.uid().clone(), f),
528 (g.uid().clone(), g),
529 (h.uid().clone(), h),
530 ]);
531 assert!(enforce_tc(&entities).is_err());
533 compute_tc_internal(&mut entities);
535 let a = &entities[&EntityUID::with_eid("A")];
536 let b = &entities[&EntityUID::with_eid("B")];
537 let d = &entities[&EntityUID::with_eid("D")];
538 let f = &entities[&EntityUID::with_eid("F")];
539 assert!(a.is_descendant_of(&EntityUID::with_eid("G")));
541 assert!(d.is_descendant_of(&EntityUID::with_eid("H")));
542 assert!(!a.is_descendant_of(&EntityUID::with_eid("C")));
544 assert!(!a.is_descendant_of(&EntityUID::with_eid("D")));
545 assert!(!a.is_descendant_of(&EntityUID::with_eid("E")));
546 assert!(!a.is_descendant_of(&EntityUID::with_eid("H")));
547 assert!(!b.is_descendant_of(&EntityUID::with_eid("E")));
548 assert!(!b.is_descendant_of(&EntityUID::with_eid("H")));
549 assert!(!f.is_descendant_of(&EntityUID::with_eid("E")));
550 assert!(!f.is_descendant_of(&EntityUID::with_eid("H")));
551 assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
552 assert!(!b.is_descendant_of(&EntityUID::with_eid("G")));
553 assert!(!f.is_descendant_of(&EntityUID::with_eid("C")));
554 assert!(!f.is_descendant_of(&EntityUID::with_eid("D")));
555 assert!(enforce_tc(&entities).is_ok());
557 assert!(enforce_dag_from_tc(&entities).is_ok());
559 }
560
561 #[test]
562 fn trivial_cycle() {
563 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
568 a.add_parent(EntityUID::with_eid("B"));
569 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
570 b.add_parent(EntityUID::with_eid("B"));
571 let mut entities = HashMap::from([(a.uid().clone(), a), (b.uid().clone(), b)]);
572 compute_tc_internal(&mut entities);
574 match enforce_dag_from_tc(&entities) {
576 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
577 Err(TcError::HasCycle(err)) => {
578 assert!(err.vertex_with_loop() == &EntityUID::with_eid("B"));
579 }
580 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
581 }
582 let a = &entities[&EntityUID::with_eid("A")];
583 let b = &entities[&EntityUID::with_eid("B")];
584 assert!(a.is_descendant_of(&EntityUID::with_eid("B")));
586 assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
588 assert!(enforce_tc(&entities).is_ok());
591 match enforce_dag_from_tc(&entities) {
593 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
594 Err(TcError::HasCycle(err)) => {
595 assert!(err.vertex_with_loop() == &EntityUID::with_eid("B"));
596 }
597 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
598 }
599 }
600
601 #[test]
602 fn nontrivial_cycle() {
603 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
610 a.add_parent(EntityUID::with_eid("B"));
611 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
612 b.add_parent(EntityUID::with_eid("C"));
613 b.add_parent(EntityUID::with_eid("D"));
614 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
615 c.add_parent(EntityUID::with_eid("A"));
616 let d = Entity::with_uid(EntityUID::with_eid("D"));
617 let mut entities = HashMap::from([
618 (a.uid().clone(), a),
619 (b.uid().clone(), b),
620 (c.uid().clone(), c),
621 (d.uid().clone(), d),
622 ]);
623 compute_tc_internal(&mut entities);
625 match enforce_dag_from_tc(&entities) {
627 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
628 Err(TcError::HasCycle(err)) => {
629 assert!(
630 err.vertex_with_loop() == &EntityUID::with_eid("A")
631 || err.vertex_with_loop() == &EntityUID::with_eid("B")
632 || err.vertex_with_loop() == &EntityUID::with_eid("C")
633 );
634 }
635 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
636 }
637 let a = &entities[&EntityUID::with_eid("A")];
639 let b = &entities[&EntityUID::with_eid("B")];
640 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
642 assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
643 assert!(b.is_descendant_of(&EntityUID::with_eid("A")));
645 assert!(enforce_tc(&entities).is_ok());
648 match enforce_dag_from_tc(&entities) {
650 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
651 Err(TcError::HasCycle(err)) => {
652 assert!(
653 err.vertex_with_loop() == &EntityUID::with_eid("A")
654 || err.vertex_with_loop() == &EntityUID::with_eid("B")
655 || err.vertex_with_loop() == &EntityUID::with_eid("C")
656 );
657 }
658 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
659 }
660 }
661
662 #[test]
663 fn disjoint_cycles() {
664 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
671 a.add_parent(EntityUID::with_eid("F"));
672 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
673 b.add_parent(EntityUID::with_eid("C"));
674 let mut c: Entity = Entity::with_uid(EntityUID::with_eid("C"));
675 c.add_parent(EntityUID::with_eid("B"));
676 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
677 d.add_parent(EntityUID::with_eid("E"));
678 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
679 e.add_parent(EntityUID::with_eid("H"));
680 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
681 f.add_parent(EntityUID::with_eid("G"));
682 let g = Entity::with_uid(EntityUID::with_eid("G"));
683 let mut h = Entity::with_uid(EntityUID::with_eid("H"));
684 h.add_parent(EntityUID::with_eid("D"));
685 let mut entities = HashMap::from([
686 (a.uid().clone(), a),
687 (b.uid().clone(), b),
688 (c.uid().clone(), c),
689 (d.uid().clone(), d),
690 (e.uid().clone(), e),
691 (f.uid().clone(), f),
692 (g.uid().clone(), g),
693 (h.uid().clone(), h),
694 ]);
695 assert!(enforce_tc(&entities).is_err());
697 compute_tc_internal(&mut entities);
699 assert!(enforce_tc(&entities).is_ok());
701 match enforce_dag_from_tc(&entities) {
703 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
704 Err(TcError::HasCycle(err)) => {
705 assert!(
707 err.vertex_with_loop() == &EntityUID::with_eid("B")
708 || err.vertex_with_loop() == &EntityUID::with_eid("C")
709 || err.vertex_with_loop() == &EntityUID::with_eid("D")
710 || err.vertex_with_loop() == &EntityUID::with_eid("E")
711 || err.vertex_with_loop() == &EntityUID::with_eid("H")
712 );
713 }
714 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
715 }
716 }
717
718 #[test]
719 fn intersecting_cycles() {
720 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
730 a.add_parent(EntityUID::with_eid("B"));
731 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
732 b.add_parent(EntityUID::with_eid("C"));
733 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
734 c.add_parent(EntityUID::with_eid("E"));
735 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
736 d.add_parent(EntityUID::with_eid("A"));
737 d.add_parent(EntityUID::with_eid("B"));
738 d.add_parent(EntityUID::with_eid("F"));
739 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
740 e.add_parent(EntityUID::with_eid("D"));
741 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
742 f.add_parent(EntityUID::with_eid("E"));
743 let mut entities = HashMap::from([
744 (a.uid().clone(), a),
745 (b.uid().clone(), b),
746 (c.uid().clone(), c),
747 (d.uid().clone(), d),
748 (e.uid().clone(), e),
749 (f.uid().clone(), f),
750 ]);
751 assert!(enforce_tc(&entities).is_err());
753 compute_tc_internal(&mut entities);
755 assert!(enforce_tc(&entities).is_ok());
757 match enforce_dag_from_tc(&entities) {
759 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
760 Err(TcError::HasCycle(_)) => (), Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
762 }
763 }
764
765 #[test]
766 fn updated() {
767 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
769 a.add_parent(EntityUID::with_eid("B"));
770 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
771 b.add_parent(EntityUID::with_eid("C"));
772 let c = Entity::with_uid(EntityUID::with_eid("C"));
773 let mut entities = HashMap::from([
774 (a.uid().clone(), a),
775 (b.uid().clone(), b),
776 (c.uid().clone(), c),
777 ]);
778 assert!(enforce_tc(&entities).is_err());
780 compute_tc_internal(&mut entities);
782 let a = &entities[&EntityUID::with_eid("A")];
783 let b = &entities[&EntityUID::with_eid("B")];
784 let c = &entities[&EntityUID::with_eid("C")];
785 assert!(a.has_edge_to(&EntityUID::with_eid("C")));
787 assert!(!b.has_edge_to(&EntityUID::with_eid("A")));
789 assert!(!c.has_edge_to(&EntityUID::with_eid("A")));
790 assert!(enforce_tc(&entities).is_ok());
792 assert!(enforce_dag_from_tc(&entities).is_ok());
794 assert!(!a.has_edge_to(&EntityUID::with_eid("D")));
796
797 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
799 b.add_parent(EntityUID::with_eid("D"));
800 let d = Entity::with_uid(EntityUID::with_eid("D"));
801 entities.insert(b.uid().clone(), b);
802 entities.insert(d.uid().clone(), d);
803
804 assert!(enforce_tc(&entities).is_err());
806 compute_tc_internal(&mut entities);
808 let a = &entities[&EntityUID::with_eid("A")];
809 let b = &entities[&EntityUID::with_eid("B")];
810 let c = &entities[&EntityUID::with_eid("C")];
811 let d = &entities[&EntityUID::with_eid("D")];
812 assert!(a.has_edge_to(&EntityUID::with_eid("D")));
814 assert!(!a.has_edge_to(&EntityUID::with_eid("C")));
816 assert!(!b.has_edge_to(&EntityUID::with_eid("C")));
817 assert!(!b.has_edge_to(&EntityUID::with_eid("A")));
819 assert!(!c.has_edge_to(&EntityUID::with_eid("A")));
820 assert!(!d.has_edge_to(&EntityUID::with_eid("A")));
821 assert!(enforce_tc(&entities).is_ok());
823 assert!(enforce_dag_from_tc(&entities).is_ok());
825 }
826}