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
48pub fn compute_tc<K, V>(nodes: &mut HashMap<K, V>, enforce_dag: bool) -> Result<(), K>
54where
55 K: Clone + Eq + Hash + Debug + Display,
56 V: TCNode<K>,
57{
58 compute_tc_internal::<K, V>(nodes);
59 if enforce_dag {
60 return enforce_dag_from_tc(nodes);
61 }
62 Ok(())
63}
64
65fn compute_tc_internal<K, V>(nodes: &mut HashMap<K, V>)
70where
71 K: Clone + Eq + Hash,
72 V: TCNode<K>,
73{
74 let mut ancestors: HashMap<K, HashSet<K>> = HashMap::new();
79 for node in nodes.values() {
80 let this_node_ancestors: &mut HashSet<K> = ancestors.entry(node.get_key()).or_default();
81 add_ancestors_to_set(node, nodes, this_node_ancestors);
82 }
83 for node in nodes.values_mut() {
84 #[allow(clippy::expect_used)]
86 for ancestor_uid in ancestors
87 .get(&node.get_key())
88 .expect("shouldn't have added any new values to the `nodes` map")
89 {
90 node.add_edge_to(ancestor_uid.clone());
91 }
92 }
93}
94
95pub fn enforce_tc_and_dag<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
100where
101 K: Clone + Eq + Hash + Debug + Display,
102 V: TCNode<K>,
103{
104 let res = enforce_tc(entities);
105 if res.is_ok() {
106 return enforce_dag_from_tc(entities);
107 }
108 res
109}
110
111fn enforce_tc<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
116where
117 K: Clone + Eq + Hash + Debug + Display,
118 V: TCNode<K>,
119{
120 for entity in entities.values() {
121 for parent_uid in entity.out_edges() {
122 if let Some(parent) = entities.get(parent_uid) {
124 for grandparent in parent.out_edges() {
125 if !entity.has_edge_to(grandparent) {
126 return Err(TcError::missing_tc_edge(
127 entity.get_key(),
128 parent_uid.clone(),
129 grandparent.clone(),
130 ));
131 }
132 }
133 }
134 }
135 }
136 Ok(())
137}
138
139fn add_ancestors_to_set<K, V>(node: &V, hierarchy: &HashMap<K, V>, ancestors: &mut HashSet<K>)
143where
144 K: Clone + Eq + Hash,
145 V: TCNode<K>,
146{
147 for ancestor_uid in node.out_edges() {
148 if ancestors.insert(ancestor_uid.clone()) {
149 if let Some(ancestor) = hierarchy.get(ancestor_uid) {
152 add_ancestors_to_set(ancestor, hierarchy, ancestors);
153 }
154 }
155 }
156}
157
158fn enforce_dag_from_tc<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
164where
165 K: Clone + Eq + Hash + Debug + Display,
166 V: TCNode<K>,
167{
168 for entity in entities.values() {
169 let key = entity.get_key();
170 if entity.out_edges().contains(&key) {
171 return Err(TcError::has_cycle(key));
172 }
173 }
174 Ok(())
175}
176
177#[allow(clippy::indexing_slicing)]
179#[allow(clippy::panic)]
181#[cfg(test)]
182mod tests {
183 use crate::ast::{Entity, EntityUID};
184
185 use super::*;
186
187 #[test]
188 fn basic() {
189 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
191 a.add_ancestor(EntityUID::with_eid("B"));
192 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
193 b.add_ancestor(EntityUID::with_eid("C"));
194 let c = Entity::with_uid(EntityUID::with_eid("C"));
195 let mut entities = HashMap::from([
196 (a.uid().clone(), a),
197 (b.uid().clone(), b),
198 (c.uid().clone(), c),
199 ]);
200 assert!(enforce_tc(&entities).is_err());
202 compute_tc_internal(&mut entities);
204 let a = &entities[&EntityUID::with_eid("A")];
205 let b = &entities[&EntityUID::with_eid("B")];
206 let c = &entities[&EntityUID::with_eid("C")];
207 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
209 assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
211 assert!(!c.is_descendant_of(&EntityUID::with_eid("A")));
212 assert!(enforce_tc(&entities).is_ok());
214 assert!(enforce_dag_from_tc(&entities).is_ok());
216 }
217
218 #[test]
219 fn reversed() {
220 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
223 a.add_ancestor(EntityUID::with_eid("B"));
224 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
225 b.add_ancestor(EntityUID::with_eid("C"));
226 let c = Entity::with_uid(EntityUID::with_eid("C"));
227 let mut entities = HashMap::from([
228 (c.uid().clone(), c),
229 (b.uid().clone(), b),
230 (a.uid().clone(), a),
231 ]);
232 assert!(enforce_tc(&entities).is_err());
234 compute_tc_internal(&mut entities);
236 let a = &entities[&EntityUID::with_eid("A")];
237 let b = &entities[&EntityUID::with_eid("B")];
238 let c = &entities[&EntityUID::with_eid("C")];
239 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
241 assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
243 assert!(!c.is_descendant_of(&EntityUID::with_eid("A")));
244 assert!(enforce_tc(&entities).is_ok());
246 assert!(enforce_dag_from_tc(&entities).is_ok());
248 }
249
250 #[test]
251 fn deeper() {
252 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
254 a.add_ancestor(EntityUID::with_eid("B"));
255 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
256 b.add_ancestor(EntityUID::with_eid("C"));
257 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
258 c.add_ancestor(EntityUID::with_eid("D"));
259 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
260 d.add_ancestor(EntityUID::with_eid("E"));
261 let e = Entity::with_uid(EntityUID::with_eid("E"));
262 let mut entities = HashMap::from([
263 (a.uid().clone(), a),
264 (b.uid().clone(), b),
265 (c.uid().clone(), c),
266 (d.uid().clone(), d),
267 (e.uid().clone(), e),
268 ]);
269 assert!(enforce_tc(&entities).is_err());
271 compute_tc_internal(&mut entities);
273 let a = &entities[&EntityUID::with_eid("A")];
274 let b = &entities[&EntityUID::with_eid("B")];
275 let c = &entities[&EntityUID::with_eid("C")];
276 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
278 assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
279 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
280 assert!(b.is_descendant_of(&EntityUID::with_eid("D")));
281 assert!(b.is_descendant_of(&EntityUID::with_eid("E")));
282 assert!(c.is_descendant_of(&EntityUID::with_eid("E")));
283 assert!(enforce_tc(&entities).is_ok());
285 assert!(enforce_dag_from_tc(&entities).is_ok());
287 }
288
289 #[test]
290 fn not_alphabetized() {
291 let mut foo = Entity::with_uid(EntityUID::with_eid("foo"));
297 foo.add_ancestor(EntityUID::with_eid("bar"));
298 let mut bar = Entity::with_uid(EntityUID::with_eid("bar"));
299 bar.add_ancestor(EntityUID::with_eid("baz"));
300 let mut baz = Entity::with_uid(EntityUID::with_eid("baz"));
301 baz.add_ancestor(EntityUID::with_eid("ham"));
302 let mut ham = Entity::with_uid(EntityUID::with_eid("ham"));
303 ham.add_ancestor(EntityUID::with_eid("eggs"));
304 let eggs = Entity::with_uid(EntityUID::with_eid("eggs"));
305 let mut entities = HashMap::from([
306 (ham.uid().clone(), ham),
307 (bar.uid().clone(), bar),
308 (foo.uid().clone(), foo),
309 (eggs.uid().clone(), eggs),
310 (baz.uid().clone(), baz),
311 ]);
312 assert!(enforce_tc(&entities).is_err());
314 compute_tc_internal(&mut entities);
316 let foo = &entities[&EntityUID::with_eid("foo")];
317 let bar = &entities[&EntityUID::with_eid("bar")];
318 let baz = &entities[&EntityUID::with_eid("baz")];
319 assert!(foo.is_descendant_of(&EntityUID::with_eid("baz")));
321 assert!(foo.is_descendant_of(&EntityUID::with_eid("ham")));
322 assert!(foo.is_descendant_of(&EntityUID::with_eid("eggs")));
323 assert!(bar.is_descendant_of(&EntityUID::with_eid("ham")));
324 assert!(bar.is_descendant_of(&EntityUID::with_eid("eggs")));
325 assert!(baz.is_descendant_of(&EntityUID::with_eid("eggs")));
326 assert!(enforce_tc(&entities).is_ok());
328 assert!(enforce_dag_from_tc(&entities).is_ok());
330 }
331
332 #[test]
333 fn multi_parents() {
334 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
341 a.add_ancestor(EntityUID::with_eid("B"));
342 a.add_ancestor(EntityUID::with_eid("D"));
343 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
344 b.add_ancestor(EntityUID::with_eid("C"));
345 let c = Entity::with_uid(EntityUID::with_eid("C"));
346 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
347 d.add_ancestor(EntityUID::with_eid("E"));
348 let e = Entity::with_uid(EntityUID::with_eid("E"));
349 let mut entities = HashMap::from([
350 (a.uid().clone(), a),
351 (b.uid().clone(), b),
352 (c.uid().clone(), c),
353 (d.uid().clone(), d),
354 (e.uid().clone(), e),
355 ]);
356 assert!(enforce_tc(&entities).is_err());
358 compute_tc_internal(&mut entities);
360 let a = &entities[&EntityUID::with_eid("A")];
361 let b = &entities[&EntityUID::with_eid("B")];
362 let d = &entities[&EntityUID::with_eid("D")];
363 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
365 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
366 assert!(!b.is_descendant_of(&EntityUID::with_eid("D")));
368 assert!(!b.is_descendant_of(&EntityUID::with_eid("E")));
369 assert!(!d.is_descendant_of(&EntityUID::with_eid("B")));
370 assert!(!d.is_descendant_of(&EntityUID::with_eid("C")));
371 assert!(enforce_tc(&entities).is_ok());
373 assert!(enforce_dag_from_tc(&entities).is_ok());
375 }
376
377 #[test]
378 fn dag() {
379 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
386 a.add_ancestor(EntityUID::with_eid("B"));
387 a.add_ancestor(EntityUID::with_eid("F"));
388 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
389 b.add_ancestor(EntityUID::with_eid("C"));
390 b.add_ancestor(EntityUID::with_eid("D"));
391 let c = Entity::with_uid(EntityUID::with_eid("C"));
392 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
393 d.add_ancestor(EntityUID::with_eid("E"));
394 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
395 e.add_ancestor(EntityUID::with_eid("H"));
396 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
397 f.add_ancestor(EntityUID::with_eid("G"));
398 let mut g = Entity::with_uid(EntityUID::with_eid("G"));
399 g.add_ancestor(EntityUID::with_eid("E"));
400 let h = Entity::with_uid(EntityUID::with_eid("H"));
401 let mut entities = HashMap::from([
402 (a.uid().clone(), a),
403 (b.uid().clone(), b),
404 (c.uid().clone(), c),
405 (d.uid().clone(), d),
406 (e.uid().clone(), e),
407 (f.uid().clone(), f),
408 (g.uid().clone(), g),
409 (h.uid().clone(), h),
410 ]);
411 assert!(enforce_tc(&entities).is_err());
413 compute_tc_internal(&mut entities);
415 let a = &entities[&EntityUID::with_eid("A")];
416 let b = &entities[&EntityUID::with_eid("B")];
417 let f = &entities[&EntityUID::with_eid("F")];
418 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
420 assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
421 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
422 assert!(a.is_descendant_of(&EntityUID::with_eid("F")));
423 assert!(a.is_descendant_of(&EntityUID::with_eid("G")));
424 assert!(a.is_descendant_of(&EntityUID::with_eid("H")));
425 assert!(b.is_descendant_of(&EntityUID::with_eid("E")));
426 assert!(b.is_descendant_of(&EntityUID::with_eid("H")));
427 assert!(f.is_descendant_of(&EntityUID::with_eid("E")));
428 assert!(f.is_descendant_of(&EntityUID::with_eid("H")));
429 assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
431 assert!(!b.is_descendant_of(&EntityUID::with_eid("G")));
432 assert!(!f.is_descendant_of(&EntityUID::with_eid("C")));
433 assert!(!f.is_descendant_of(&EntityUID::with_eid("D")));
434 assert!(enforce_tc(&entities).is_ok());
436 assert!(enforce_dag_from_tc(&entities).is_ok());
438 }
439
440 #[test]
441 fn already_edges() {
442 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
450 a.add_ancestor(EntityUID::with_eid("B"));
451 a.add_ancestor(EntityUID::with_eid("C"));
452 a.add_ancestor(EntityUID::with_eid("D"));
453 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
454 b.add_ancestor(EntityUID::with_eid("C"));
455 b.add_ancestor(EntityUID::with_eid("E"));
456 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
457 c.add_ancestor(EntityUID::with_eid("E"));
458 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
459 d.add_ancestor(EntityUID::with_eid("C"));
460 d.add_ancestor(EntityUID::with_eid("F"));
461 let e = Entity::with_uid(EntityUID::with_eid("E"));
462 let f = Entity::with_uid(EntityUID::with_eid("F"));
463 let mut entities = HashMap::from([
464 (a.uid().clone(), a),
465 (b.uid().clone(), b),
466 (c.uid().clone(), c),
467 (d.uid().clone(), d),
468 (e.uid().clone(), e),
469 (f.uid().clone(), f),
470 ]);
471 assert!(enforce_tc(&entities).is_err());
473 compute_tc_internal(&mut entities);
475 let a = &entities[&EntityUID::with_eid("A")];
476 let b = &entities[&EntityUID::with_eid("B")];
477 let c = &entities[&EntityUID::with_eid("C")];
478 let d = &entities[&EntityUID::with_eid("D")];
479 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
481 assert!(a.is_descendant_of(&EntityUID::with_eid("F")));
482 assert!(d.is_descendant_of(&EntityUID::with_eid("E")));
483 assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
485 assert!(!c.is_descendant_of(&EntityUID::with_eid("F")));
486 assert!(enforce_tc(&entities).is_ok());
488 assert!(enforce_dag_from_tc(&entities).is_ok());
490 }
491
492 #[test]
493 fn disjoint_dag() {
494 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
501 a.add_ancestor(EntityUID::with_eid("F"));
502 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
503 b.add_ancestor(EntityUID::with_eid("C"));
504 let c = Entity::with_uid(EntityUID::with_eid("C"));
505 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
506 d.add_ancestor(EntityUID::with_eid("E"));
507 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
508 e.add_ancestor(EntityUID::with_eid("H"));
509 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
510 f.add_ancestor(EntityUID::with_eid("G"));
511 let g = Entity::with_uid(EntityUID::with_eid("G"));
512 let h = Entity::with_uid(EntityUID::with_eid("H"));
513 let mut entities = HashMap::from([
514 (a.uid().clone(), a),
515 (b.uid().clone(), b),
516 (c.uid().clone(), c),
517 (d.uid().clone(), d),
518 (e.uid().clone(), e),
519 (f.uid().clone(), f),
520 (g.uid().clone(), g),
521 (h.uid().clone(), h),
522 ]);
523 assert!(enforce_tc(&entities).is_err());
525 compute_tc_internal(&mut entities);
527 let a = &entities[&EntityUID::with_eid("A")];
528 let b = &entities[&EntityUID::with_eid("B")];
529 let d = &entities[&EntityUID::with_eid("D")];
530 let f = &entities[&EntityUID::with_eid("F")];
531 assert!(a.is_descendant_of(&EntityUID::with_eid("G")));
533 assert!(d.is_descendant_of(&EntityUID::with_eid("H")));
534 assert!(!a.is_descendant_of(&EntityUID::with_eid("C")));
536 assert!(!a.is_descendant_of(&EntityUID::with_eid("D")));
537 assert!(!a.is_descendant_of(&EntityUID::with_eid("E")));
538 assert!(!a.is_descendant_of(&EntityUID::with_eid("H")));
539 assert!(!b.is_descendant_of(&EntityUID::with_eid("E")));
540 assert!(!b.is_descendant_of(&EntityUID::with_eid("H")));
541 assert!(!f.is_descendant_of(&EntityUID::with_eid("E")));
542 assert!(!f.is_descendant_of(&EntityUID::with_eid("H")));
543 assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
544 assert!(!b.is_descendant_of(&EntityUID::with_eid("G")));
545 assert!(!f.is_descendant_of(&EntityUID::with_eid("C")));
546 assert!(!f.is_descendant_of(&EntityUID::with_eid("D")));
547 assert!(enforce_tc(&entities).is_ok());
549 assert!(enforce_dag_from_tc(&entities).is_ok());
551 }
552
553 #[test]
554 fn trivial_cycle() {
555 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
560 a.add_ancestor(EntityUID::with_eid("B"));
561 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
562 b.add_ancestor(EntityUID::with_eid("B"));
563 let mut entities = HashMap::from([(a.uid().clone(), a), (b.uid().clone(), b)]);
564 compute_tc_internal(&mut entities);
566 match enforce_dag_from_tc(&entities) {
568 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
569 Err(TcError::HasCycle(err)) => {
570 assert!(err.vertex_with_loop() == &EntityUID::with_eid("B"));
571 }
572 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
573 }
574 let a = &entities[&EntityUID::with_eid("A")];
575 let b = &entities[&EntityUID::with_eid("B")];
576 assert!(a.is_descendant_of(&EntityUID::with_eid("B")));
578 assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
580 assert!(enforce_tc(&entities).is_ok());
583 match enforce_dag_from_tc(&entities) {
585 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
586 Err(TcError::HasCycle(err)) => {
587 assert!(err.vertex_with_loop() == &EntityUID::with_eid("B"));
588 }
589 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
590 }
591 }
592
593 #[test]
594 fn nontrivial_cycle() {
595 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
602 a.add_ancestor(EntityUID::with_eid("B"));
603 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
604 b.add_ancestor(EntityUID::with_eid("C"));
605 b.add_ancestor(EntityUID::with_eid("D"));
606 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
607 c.add_ancestor(EntityUID::with_eid("A"));
608 let d = Entity::with_uid(EntityUID::with_eid("D"));
609 let mut entities = HashMap::from([
610 (a.uid().clone(), a),
611 (b.uid().clone(), b),
612 (c.uid().clone(), c),
613 (d.uid().clone(), d),
614 ]);
615 compute_tc_internal(&mut entities);
617 match enforce_dag_from_tc(&entities) {
619 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
620 Err(TcError::HasCycle(err)) => {
621 assert!(
622 err.vertex_with_loop() == &EntityUID::with_eid("A")
623 || err.vertex_with_loop() == &EntityUID::with_eid("B")
624 || err.vertex_with_loop() == &EntityUID::with_eid("C")
625 );
626 }
627 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
628 }
629 let a = &entities[&EntityUID::with_eid("A")];
631 let b = &entities[&EntityUID::with_eid("B")];
632 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
634 assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
635 assert!(b.is_descendant_of(&EntityUID::with_eid("A")));
637 assert!(enforce_tc(&entities).is_ok());
640 match enforce_dag_from_tc(&entities) {
642 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
643 Err(TcError::HasCycle(err)) => {
644 assert!(
645 err.vertex_with_loop() == &EntityUID::with_eid("A")
646 || err.vertex_with_loop() == &EntityUID::with_eid("B")
647 || err.vertex_with_loop() == &EntityUID::with_eid("C")
648 );
649 }
650 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
651 }
652 }
653
654 #[test]
655 fn disjoint_cycles() {
656 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
663 a.add_ancestor(EntityUID::with_eid("F"));
664 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
665 b.add_ancestor(EntityUID::with_eid("C"));
666 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
667 c.add_ancestor(EntityUID::with_eid("B"));
668 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
669 d.add_ancestor(EntityUID::with_eid("E"));
670 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
671 e.add_ancestor(EntityUID::with_eid("H"));
672 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
673 f.add_ancestor(EntityUID::with_eid("G"));
674 let g = Entity::with_uid(EntityUID::with_eid("G"));
675 let mut h = Entity::with_uid(EntityUID::with_eid("H"));
676 h.add_ancestor(EntityUID::with_eid("D"));
677 let mut entities = HashMap::from([
678 (a.uid().clone(), a),
679 (b.uid().clone(), b),
680 (c.uid().clone(), c),
681 (d.uid().clone(), d),
682 (e.uid().clone(), e),
683 (f.uid().clone(), f),
684 (g.uid().clone(), g),
685 (h.uid().clone(), h),
686 ]);
687 assert!(enforce_tc(&entities).is_err());
689 compute_tc_internal(&mut entities);
691 assert!(enforce_tc(&entities).is_ok());
693 match enforce_dag_from_tc(&entities) {
695 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
696 Err(TcError::HasCycle(err)) => {
697 assert!(
699 err.vertex_with_loop() == &EntityUID::with_eid("B")
700 || err.vertex_with_loop() == &EntityUID::with_eid("C")
701 || err.vertex_with_loop() == &EntityUID::with_eid("D")
702 || err.vertex_with_loop() == &EntityUID::with_eid("E")
703 || err.vertex_with_loop() == &EntityUID::with_eid("H")
704 );
705 }
706 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
707 }
708 }
709
710 #[test]
711 fn intersecting_cycles() {
712 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
722 a.add_ancestor(EntityUID::with_eid("B"));
723 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
724 b.add_ancestor(EntityUID::with_eid("C"));
725 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
726 c.add_ancestor(EntityUID::with_eid("E"));
727 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
728 d.add_ancestor(EntityUID::with_eid("A"));
729 d.add_ancestor(EntityUID::with_eid("B"));
730 d.add_ancestor(EntityUID::with_eid("F"));
731 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
732 e.add_ancestor(EntityUID::with_eid("D"));
733 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
734 f.add_ancestor(EntityUID::with_eid("E"));
735 let mut entities = HashMap::from([
736 (a.uid().clone(), a),
737 (b.uid().clone(), b),
738 (c.uid().clone(), c),
739 (d.uid().clone(), d),
740 (e.uid().clone(), e),
741 (f.uid().clone(), f),
742 ]);
743 assert!(enforce_tc(&entities).is_err());
745 compute_tc_internal(&mut entities);
747 assert!(enforce_tc(&entities).is_ok());
749 match enforce_dag_from_tc(&entities) {
751 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
752 Err(TcError::HasCycle(_)) => (), Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
754 }
755 }
756}