1use std::cmp::Eq;
21use std::collections::{HashMap, HashSet};
22use std::fmt::{Debug, Display};
23use std::hash::Hash;
24
25mod err;
26pub use err::*;
27use itertools::Itertools;
28
29pub trait TCNode<K> {
35 fn get_key(&self) -> K;
37
38 fn add_edge_to(&mut self, k: K);
40
41 fn out_edges(&self) -> Box<dyn Iterator<Item = &K> + '_>;
43
44 fn has_edge_to(&self, k: &K) -> bool;
47}
48
49pub fn compute_tc<K, V>(nodes: &mut HashMap<K, V>, enforce_dag: bool) -> Result<(), K>
55where
56 K: Clone + Eq + Hash + Debug + Display,
57 V: TCNode<K>,
58{
59 let res = compute_tc_internal::<K, V>(nodes);
60 if res.is_ok() && enforce_dag {
61 return enforce_dag_from_tc(nodes);
62 }
63 res
64}
65
66fn compute_tc_internal<K, V>(nodes: &mut HashMap<K, V>) -> Result<(), K>
71where
72 K: Clone + Eq + Hash + Debug + Display,
73 V: TCNode<K>,
74{
75 let mut ancestors: HashMap<K, HashSet<K>> = HashMap::new();
80 for node in nodes.values() {
81 let this_node_ancestors: &mut HashSet<K> = ancestors.entry(node.get_key()).or_default();
82 add_ancestors_to_set(node, nodes, this_node_ancestors)?;
83 }
84 for node in nodes.values_mut() {
85 for ancestor_uid in ancestors
86 .get(&node.get_key())
87 .expect("shouldn't have added any new values to the `nodes` map")
88 {
89 node.add_edge_to(ancestor_uid.clone());
90 }
91 }
92 Ok(())
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(Err::TCEnforcementError {
127 child: entity.get_key(),
128 parent: parent_uid.clone(),
129 grandparent: grandparent.clone(),
130 });
131 }
132 }
133 }
134 }
135 }
136 Ok(())
137}
138
139fn add_ancestors_to_set<K, V>(
143 node: &V,
144 hierarchy: &HashMap<K, V>,
145 ancestors: &mut HashSet<K>,
146) -> Result<(), K>
147where
148 K: Clone + Eq + Hash + Debug + Display,
149 V: TCNode<K>,
150{
151 for ancestor_uid in node.out_edges() {
152 if ancestors.insert(ancestor_uid.clone()) {
153 if let Some(ancestor) = hierarchy.get(ancestor_uid) {
156 add_ancestors_to_set(ancestor, hierarchy, ancestors)?;
157 }
158 }
159 }
160 Ok(())
161}
162
163fn enforce_dag_from_tc<K, V>(entities: &HashMap<K, V>) -> Result<(), K>
169where
170 K: Clone + Eq + Hash + Debug + Display,
171 V: TCNode<K>,
172{
173 for entity in entities.values() {
174 let key = entity.get_key();
175 if entity.out_edges().contains(&key) {
176 return Err(Err::HasCycle {
177 vertex_with_loop: key,
178 });
179 }
180 }
181 Ok(())
182}
183
184#[cfg(test)]
185mod tests {
186 use crate::ast::{Entity, EntityUID};
187
188 use super::*;
189
190 #[test]
191 fn basic() {
192 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
194 a.add_ancestor(EntityUID::with_eid("B"));
195 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
196 b.add_ancestor(EntityUID::with_eid("C"));
197 let c = Entity::with_uid(EntityUID::with_eid("C"));
198 let mut entities = HashMap::from([(a.uid(), a), (b.uid(), b), (c.uid(), c)]);
199 assert!(enforce_tc(&entities).is_err());
201 assert!(compute_tc_internal(&mut entities).is_ok());
203 let a = &entities[&EntityUID::with_eid("A")];
204 let b = &entities[&EntityUID::with_eid("B")];
205 let c = &entities[&EntityUID::with_eid("C")];
206 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
208 assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
210 assert!(!c.is_descendant_of(&EntityUID::with_eid("A")));
211 assert!(enforce_tc(&entities).is_ok());
213 assert!(enforce_dag_from_tc(&entities).is_ok());
215 }
216
217 #[test]
218 fn reversed() {
219 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
222 a.add_ancestor(EntityUID::with_eid("B"));
223 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
224 b.add_ancestor(EntityUID::with_eid("C"));
225 let c = Entity::with_uid(EntityUID::with_eid("C"));
226 let mut entities = HashMap::from([(c.uid(), c), (b.uid(), b), (a.uid(), a)]);
227 assert!(enforce_tc(&entities).is_err());
229 assert!(compute_tc_internal(&mut entities).is_ok());
231 let a = &entities[&EntityUID::with_eid("A")];
232 let b = &entities[&EntityUID::with_eid("B")];
233 let c = &entities[&EntityUID::with_eid("C")];
234 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
236 assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
238 assert!(!c.is_descendant_of(&EntityUID::with_eid("A")));
239 assert!(enforce_tc(&entities).is_ok());
241 assert!(enforce_dag_from_tc(&entities).is_ok());
243 }
244
245 #[test]
246 fn deeper() {
247 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
249 a.add_ancestor(EntityUID::with_eid("B"));
250 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
251 b.add_ancestor(EntityUID::with_eid("C"));
252 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
253 c.add_ancestor(EntityUID::with_eid("D"));
254 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
255 d.add_ancestor(EntityUID::with_eid("E"));
256 let e = Entity::with_uid(EntityUID::with_eid("E"));
257 let mut entities = HashMap::from([
258 (a.uid(), a),
259 (b.uid(), b),
260 (c.uid(), c),
261 (d.uid(), d),
262 (e.uid(), e),
263 ]);
264 assert!(enforce_tc(&entities).is_err());
266 assert!(compute_tc_internal(&mut entities).is_ok());
268 let a = &entities[&EntityUID::with_eid("A")];
269 let b = &entities[&EntityUID::with_eid("B")];
270 let c = &entities[&EntityUID::with_eid("C")];
271 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
273 assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
274 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
275 assert!(b.is_descendant_of(&EntityUID::with_eid("D")));
276 assert!(b.is_descendant_of(&EntityUID::with_eid("E")));
277 assert!(c.is_descendant_of(&EntityUID::with_eid("E")));
278 assert!(enforce_tc(&entities).is_ok());
280 assert!(enforce_dag_from_tc(&entities).is_ok());
282 }
283
284 #[test]
285 fn not_alphabetized() {
286 let mut foo = Entity::with_uid(EntityUID::with_eid("foo"));
292 foo.add_ancestor(EntityUID::with_eid("bar"));
293 let mut bar = Entity::with_uid(EntityUID::with_eid("bar"));
294 bar.add_ancestor(EntityUID::with_eid("baz"));
295 let mut baz = Entity::with_uid(EntityUID::with_eid("baz"));
296 baz.add_ancestor(EntityUID::with_eid("ham"));
297 let mut ham = Entity::with_uid(EntityUID::with_eid("ham"));
298 ham.add_ancestor(EntityUID::with_eid("eggs"));
299 let eggs = Entity::with_uid(EntityUID::with_eid("eggs"));
300 let mut entities = HashMap::from([
301 (ham.uid(), ham),
302 (bar.uid(), bar),
303 (foo.uid(), foo),
304 (eggs.uid(), eggs),
305 (baz.uid(), baz),
306 ]);
307 assert!(enforce_tc(&entities).is_err());
309 assert!(compute_tc_internal(&mut entities).is_ok());
311 let foo = &entities[&EntityUID::with_eid("foo")];
312 let bar = &entities[&EntityUID::with_eid("bar")];
313 let baz = &entities[&EntityUID::with_eid("baz")];
314 assert!(foo.is_descendant_of(&EntityUID::with_eid("baz")));
316 assert!(foo.is_descendant_of(&EntityUID::with_eid("ham")));
317 assert!(foo.is_descendant_of(&EntityUID::with_eid("eggs")));
318 assert!(bar.is_descendant_of(&EntityUID::with_eid("ham")));
319 assert!(bar.is_descendant_of(&EntityUID::with_eid("eggs")));
320 assert!(baz.is_descendant_of(&EntityUID::with_eid("eggs")));
321 assert!(enforce_tc(&entities).is_ok());
323 assert!(enforce_dag_from_tc(&entities).is_ok());
325 }
326
327 #[test]
328 fn multi_parents() {
329 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
336 a.add_ancestor(EntityUID::with_eid("B"));
337 a.add_ancestor(EntityUID::with_eid("D"));
338 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
339 b.add_ancestor(EntityUID::with_eid("C"));
340 let c = Entity::with_uid(EntityUID::with_eid("C"));
341 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
342 d.add_ancestor(EntityUID::with_eid("E"));
343 let e = Entity::with_uid(EntityUID::with_eid("E"));
344 let mut entities = HashMap::from([
345 (a.uid(), a),
346 (b.uid(), b),
347 (c.uid(), c),
348 (d.uid(), d),
349 (e.uid(), e),
350 ]);
351 assert!(enforce_tc(&entities).is_err());
353 assert!(compute_tc_internal(&mut entities).is_ok());
355 let a = &entities[&EntityUID::with_eid("A")];
356 let b = &entities[&EntityUID::with_eid("B")];
357 let d = &entities[&EntityUID::with_eid("D")];
358 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
360 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
361 assert!(!b.is_descendant_of(&EntityUID::with_eid("D")));
363 assert!(!b.is_descendant_of(&EntityUID::with_eid("E")));
364 assert!(!d.is_descendant_of(&EntityUID::with_eid("B")));
365 assert!(!d.is_descendant_of(&EntityUID::with_eid("C")));
366 assert!(enforce_tc(&entities).is_ok());
368 assert!(enforce_dag_from_tc(&entities).is_ok());
370 }
371
372 #[test]
373 fn dag() {
374 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
381 a.add_ancestor(EntityUID::with_eid("B"));
382 a.add_ancestor(EntityUID::with_eid("F"));
383 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
384 b.add_ancestor(EntityUID::with_eid("C"));
385 b.add_ancestor(EntityUID::with_eid("D"));
386 let c = Entity::with_uid(EntityUID::with_eid("C"));
387 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
388 d.add_ancestor(EntityUID::with_eid("E"));
389 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
390 e.add_ancestor(EntityUID::with_eid("H"));
391 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
392 f.add_ancestor(EntityUID::with_eid("G"));
393 let mut g = Entity::with_uid(EntityUID::with_eid("G"));
394 g.add_ancestor(EntityUID::with_eid("E"));
395 let h = Entity::with_uid(EntityUID::with_eid("H"));
396 let mut entities = HashMap::from([
397 (a.uid(), a),
398 (b.uid(), b),
399 (c.uid(), c),
400 (d.uid(), d),
401 (e.uid(), e),
402 (f.uid(), f),
403 (g.uid(), g),
404 (h.uid(), h),
405 ]);
406 assert!(enforce_tc(&entities).is_err());
408 assert!(compute_tc_internal(&mut entities).is_ok());
410 let a = &entities[&EntityUID::with_eid("A")];
411 let b = &entities[&EntityUID::with_eid("B")];
412 let f = &entities[&EntityUID::with_eid("F")];
413 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
415 assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
416 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
417 assert!(a.is_descendant_of(&EntityUID::with_eid("F")));
418 assert!(a.is_descendant_of(&EntityUID::with_eid("G")));
419 assert!(a.is_descendant_of(&EntityUID::with_eid("H")));
420 assert!(b.is_descendant_of(&EntityUID::with_eid("E")));
421 assert!(b.is_descendant_of(&EntityUID::with_eid("H")));
422 assert!(f.is_descendant_of(&EntityUID::with_eid("E")));
423 assert!(f.is_descendant_of(&EntityUID::with_eid("H")));
424 assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
426 assert!(!b.is_descendant_of(&EntityUID::with_eid("G")));
427 assert!(!f.is_descendant_of(&EntityUID::with_eid("C")));
428 assert!(!f.is_descendant_of(&EntityUID::with_eid("D")));
429 assert!(enforce_tc(&entities).is_ok());
431 assert!(enforce_dag_from_tc(&entities).is_ok());
433 }
434
435 #[test]
436 fn already_edges() {
437 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
445 a.add_ancestor(EntityUID::with_eid("B"));
446 a.add_ancestor(EntityUID::with_eid("C"));
447 a.add_ancestor(EntityUID::with_eid("D"));
448 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
449 b.add_ancestor(EntityUID::with_eid("C"));
450 b.add_ancestor(EntityUID::with_eid("E"));
451 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
452 c.add_ancestor(EntityUID::with_eid("E"));
453 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
454 d.add_ancestor(EntityUID::with_eid("C"));
455 d.add_ancestor(EntityUID::with_eid("F"));
456 let e = Entity::with_uid(EntityUID::with_eid("E"));
457 let f = Entity::with_uid(EntityUID::with_eid("F"));
458 let mut entities = HashMap::from([
459 (a.uid(), a),
460 (b.uid(), b),
461 (c.uid(), c),
462 (d.uid(), d),
463 (e.uid(), e),
464 (f.uid(), f),
465 ]);
466 assert!(enforce_tc(&entities).is_err());
468 assert!(compute_tc_internal(&mut entities).is_ok());
470 let a = &entities[&EntityUID::with_eid("A")];
471 let b = &entities[&EntityUID::with_eid("B")];
472 let c = &entities[&EntityUID::with_eid("C")];
473 let d = &entities[&EntityUID::with_eid("D")];
474 assert!(a.is_descendant_of(&EntityUID::with_eid("E")));
476 assert!(a.is_descendant_of(&EntityUID::with_eid("F")));
477 assert!(d.is_descendant_of(&EntityUID::with_eid("E")));
478 assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
480 assert!(!c.is_descendant_of(&EntityUID::with_eid("F")));
481 assert!(enforce_tc(&entities).is_ok());
483 assert!(enforce_dag_from_tc(&entities).is_ok());
485 }
486
487 #[test]
488 fn disjoint_dag() {
489 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
496 a.add_ancestor(EntityUID::with_eid("F"));
497 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
498 b.add_ancestor(EntityUID::with_eid("C"));
499 let c = Entity::with_uid(EntityUID::with_eid("C"));
500 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
501 d.add_ancestor(EntityUID::with_eid("E"));
502 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
503 e.add_ancestor(EntityUID::with_eid("H"));
504 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
505 f.add_ancestor(EntityUID::with_eid("G"));
506 let g = Entity::with_uid(EntityUID::with_eid("G"));
507 let h = Entity::with_uid(EntityUID::with_eid("H"));
508 let mut entities = HashMap::from([
509 (a.uid(), a),
510 (b.uid(), b),
511 (c.uid(), c),
512 (d.uid(), d),
513 (e.uid(), e),
514 (f.uid(), f),
515 (g.uid(), g),
516 (h.uid(), h),
517 ]);
518 assert!(enforce_tc(&entities).is_err());
520 assert!(compute_tc_internal(&mut entities).is_ok());
522 let a = &entities[&EntityUID::with_eid("A")];
523 let b = &entities[&EntityUID::with_eid("B")];
524 let d = &entities[&EntityUID::with_eid("D")];
525 let f = &entities[&EntityUID::with_eid("F")];
526 assert!(a.is_descendant_of(&EntityUID::with_eid("G")));
528 assert!(d.is_descendant_of(&EntityUID::with_eid("H")));
529 assert!(!a.is_descendant_of(&EntityUID::with_eid("C")));
531 assert!(!a.is_descendant_of(&EntityUID::with_eid("D")));
532 assert!(!a.is_descendant_of(&EntityUID::with_eid("E")));
533 assert!(!a.is_descendant_of(&EntityUID::with_eid("H")));
534 assert!(!b.is_descendant_of(&EntityUID::with_eid("E")));
535 assert!(!b.is_descendant_of(&EntityUID::with_eid("H")));
536 assert!(!f.is_descendant_of(&EntityUID::with_eid("E")));
537 assert!(!f.is_descendant_of(&EntityUID::with_eid("H")));
538 assert!(!b.is_descendant_of(&EntityUID::with_eid("F")));
539 assert!(!b.is_descendant_of(&EntityUID::with_eid("G")));
540 assert!(!f.is_descendant_of(&EntityUID::with_eid("C")));
541 assert!(!f.is_descendant_of(&EntityUID::with_eid("D")));
542 assert!(enforce_tc(&entities).is_ok());
544 assert!(enforce_dag_from_tc(&entities).is_ok());
546 }
547
548 #[test]
549 fn trivial_cycle() {
550 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
555 a.add_ancestor(EntityUID::with_eid("B"));
556 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
557 b.add_ancestor(EntityUID::with_eid("B"));
558 let mut entities = HashMap::from([(a.uid(), a), (b.uid(), b)]);
559 assert!(compute_tc_internal(&mut entities).is_ok());
561 match enforce_dag_from_tc(&entities) {
563 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
564 Err(Err::HasCycle { vertex_with_loop }) => {
565 assert!(vertex_with_loop == EntityUID::with_eid("B"));
566 }
567 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
568 }
569 let a = &entities[&EntityUID::with_eid("A")];
570 let b = &entities[&EntityUID::with_eid("B")];
571 assert!(a.is_descendant_of(&EntityUID::with_eid("B")));
573 assert!(!b.is_descendant_of(&EntityUID::with_eid("A")));
575 assert!(enforce_tc(&entities).is_ok());
578 match enforce_dag_from_tc(&entities) {
580 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
581 Err(Err::HasCycle { vertex_with_loop }) => {
582 assert!(vertex_with_loop == EntityUID::with_eid("B"));
583 }
584 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
585 }
586 }
587
588 #[test]
589 fn nontrivial_cycle() {
590 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
597 a.add_ancestor(EntityUID::with_eid("B"));
598 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
599 b.add_ancestor(EntityUID::with_eid("C"));
600 b.add_ancestor(EntityUID::with_eid("D"));
601 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
602 c.add_ancestor(EntityUID::with_eid("A"));
603 let d = Entity::with_uid(EntityUID::with_eid("D"));
604 let mut entities = HashMap::from([(a.uid(), a), (b.uid(), b), (c.uid(), c), (d.uid(), d)]);
605 assert!(compute_tc_internal(&mut entities).is_ok());
607 match enforce_dag_from_tc(&entities) {
609 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
610 Err(Err::HasCycle { vertex_with_loop }) => {
611 assert!(
612 vertex_with_loop == EntityUID::with_eid("A")
613 || vertex_with_loop == EntityUID::with_eid("B")
614 || vertex_with_loop == EntityUID::with_eid("C")
615 );
616 }
617 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
618 }
619 let a = &entities[&EntityUID::with_eid("A")];
621 let b = &entities[&EntityUID::with_eid("B")];
622 assert!(a.is_descendant_of(&EntityUID::with_eid("C")));
624 assert!(a.is_descendant_of(&EntityUID::with_eid("D")));
625 assert!(b.is_descendant_of(&EntityUID::with_eid("A")));
627 assert!(enforce_tc(&entities).is_ok());
630 match enforce_dag_from_tc(&entities) {
632 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
633 Err(Err::HasCycle { vertex_with_loop }) => {
634 assert!(
635 vertex_with_loop == EntityUID::with_eid("A")
636 || vertex_with_loop == EntityUID::with_eid("B")
637 || vertex_with_loop == EntityUID::with_eid("C")
638 );
639 }
640 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
641 }
642 }
643
644 #[test]
645 fn disjoint_cycles() {
646 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
653 a.add_ancestor(EntityUID::with_eid("F"));
654 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
655 b.add_ancestor(EntityUID::with_eid("C"));
656 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
657 c.add_ancestor(EntityUID::with_eid("B"));
658 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
659 d.add_ancestor(EntityUID::with_eid("E"));
660 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
661 e.add_ancestor(EntityUID::with_eid("H"));
662 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
663 f.add_ancestor(EntityUID::with_eid("G"));
664 let g = Entity::with_uid(EntityUID::with_eid("G"));
665 let mut h = Entity::with_uid(EntityUID::with_eid("H"));
666 h.add_ancestor(EntityUID::with_eid("D"));
667 let mut entities = HashMap::from([
668 (a.uid(), a),
669 (b.uid(), b),
670 (c.uid(), c),
671 (d.uid(), d),
672 (e.uid(), e),
673 (f.uid(), f),
674 (g.uid(), g),
675 (h.uid(), h),
676 ]);
677 assert!(enforce_tc(&entities).is_err());
679 assert!(compute_tc_internal(&mut entities).is_ok());
681 assert!(enforce_tc(&entities).is_ok());
683 match enforce_dag_from_tc(&entities) {
685 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
686 Err(Err::HasCycle { vertex_with_loop }) => {
687 assert!(
689 vertex_with_loop == EntityUID::with_eid("B")
690 || vertex_with_loop == EntityUID::with_eid("C")
691 || vertex_with_loop == EntityUID::with_eid("D")
692 || vertex_with_loop == EntityUID::with_eid("E")
693 || vertex_with_loop == EntityUID::with_eid("H")
694 );
695 }
696 Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
697 }
698 }
699
700 #[test]
701 fn intersecting_cycles() {
702 let mut a = Entity::with_uid(EntityUID::with_eid("A"));
712 a.add_ancestor(EntityUID::with_eid("B"));
713 let mut b = Entity::with_uid(EntityUID::with_eid("B"));
714 b.add_ancestor(EntityUID::with_eid("C"));
715 let mut c = Entity::with_uid(EntityUID::with_eid("C"));
716 c.add_ancestor(EntityUID::with_eid("E"));
717 let mut d = Entity::with_uid(EntityUID::with_eid("D"));
718 d.add_ancestor(EntityUID::with_eid("A"));
719 d.add_ancestor(EntityUID::with_eid("B"));
720 d.add_ancestor(EntityUID::with_eid("F"));
721 let mut e = Entity::with_uid(EntityUID::with_eid("E"));
722 e.add_ancestor(EntityUID::with_eid("D"));
723 let mut f = Entity::with_uid(EntityUID::with_eid("F"));
724 f.add_ancestor(EntityUID::with_eid("E"));
725 let mut entities = HashMap::from([
726 (a.uid(), a),
727 (b.uid(), b),
728 (c.uid(), c),
729 (d.uid(), d),
730 (e.uid(), e),
731 (f.uid(), f),
732 ]);
733 assert!(enforce_tc(&entities).is_err());
735 assert!(compute_tc_internal(&mut entities).is_ok());
737 assert!(enforce_tc(&entities).is_ok());
739 match enforce_dag_from_tc(&entities) {
741 Ok(_) => panic!("enforce_dag_from_tc should have returned an error"),
742 Err(Err::HasCycle {
743 vertex_with_loop: _,
744 }) => (), Err(_) => panic!("Unexpected error in enforce_dag_from_tc"),
746 }
747 }
748}