1use std::collections::VecDeque;
62use std::fmt;
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
72pub struct NodeId(u32);
73
74impl NodeId {
75 const NONE: u32 = u32::MAX;
77
78 #[must_use]
80 pub fn from_raw(index: u32) -> Self {
81 Self(index)
82 }
83
84 #[must_use]
86 pub fn raw(self) -> u32 {
87 self.0
88 }
89}
90
91impl fmt::Display for NodeId {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 write!(f, "N{}", self.0)
94 }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub enum InputKind {
104 Constraint,
106 Content,
108 Style,
110}
111
112#[derive(Clone)]
121struct DepNode {
122 generation: u32,
124 dirty_gen: u32,
127 constraint_hash: u64,
129 content_hash: u64,
131 style_hash: u64,
133 parent: u32,
135}
136
137impl DepNode {
138 fn new(generation: u32) -> Self {
139 Self {
140 generation,
141 dirty_gen: 0,
142 constraint_hash: 0,
143 content_hash: 0,
144 style_hash: 0,
145 parent: NodeId::NONE,
146 }
147 }
148
149 fn is_dirty(&self) -> bool {
150 self.dirty_gen >= self.generation
151 }
152}
153
154#[derive(Debug, Clone, PartialEq, Eq)]
160pub struct CycleError {
161 pub from: NodeId,
162 pub to: NodeId,
163}
164
165impl fmt::Display for CycleError {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 write!(
168 f,
169 "layout cycle detected: {} → {} would create a cycle",
170 self.from, self.to
171 )
172 }
173}
174
175impl std::error::Error for CycleError {}
176
177pub struct DepGraph {
205 nodes: Vec<DepNode>,
206 fwd_adj: Vec<Vec<NodeId>>,
208 rev_adj: Vec<Vec<NodeId>>,
210 current_gen: u32,
212 pending_dirty: Vec<NodeId>,
214 free_list: Vec<u32>,
216}
217
218impl DepGraph {
219 #[must_use]
221 pub fn new() -> Self {
222 Self {
223 nodes: Vec::new(),
224 fwd_adj: Vec::new(),
225 rev_adj: Vec::new(),
226 current_gen: 1,
227 pending_dirty: Vec::new(),
228 free_list: Vec::new(),
229 }
230 }
231
232 #[must_use]
234 pub fn with_capacity(node_cap: usize, _edge_cap: usize) -> Self {
235 Self {
236 nodes: Vec::with_capacity(node_cap),
237 fwd_adj: Vec::with_capacity(node_cap),
238 rev_adj: Vec::with_capacity(node_cap),
239 current_gen: 1,
240 pending_dirty: Vec::new(),
241 free_list: Vec::new(),
242 }
243 }
244
245 pub fn add_node(&mut self) -> NodeId {
247 if let Some(slot) = self.free_list.pop() {
248 self.nodes[slot as usize] = DepNode::new(self.current_gen);
249 self.fwd_adj[slot as usize].clear();
250 self.rev_adj[slot as usize].clear();
251 NodeId(slot)
252 } else {
253 let id = self.nodes.len() as u32;
254 self.nodes.push(DepNode::new(self.current_gen));
255 self.fwd_adj.push(Vec::new());
256 self.rev_adj.push(Vec::new());
257 NodeId(id)
258 }
259 }
260
261 pub fn remove_node(&mut self, id: NodeId) {
263 let idx = id.0 as usize;
264 if idx < self.nodes.len() {
265 self.nodes[idx].generation = 0;
266 self.nodes[idx].dirty_gen = 0;
267 self.fwd_adj[idx].clear();
268 self.rev_adj[idx].clear();
269 self.free_list.push(id.0);
270 }
271 }
272
273 #[must_use]
275 pub fn node_count(&self) -> usize {
276 self.nodes.len() - self.free_list.len()
277 }
278
279 #[must_use]
281 pub fn edge_count(&self) -> usize {
282 self.fwd_adj.iter().map(|v| v.len()).sum()
283 }
284
285 pub fn set_parent(&mut self, child: NodeId, parent: NodeId) {
287 if (child.0 as usize) < self.nodes.len() {
288 self.nodes[child.0 as usize].parent = parent.0;
289 }
290 }
291
292 #[must_use]
294 pub fn parent(&self, id: NodeId) -> Option<NodeId> {
295 let node = self.nodes.get(id.0 as usize)?;
296 if node.parent == NodeId::NONE {
297 None
298 } else {
299 Some(NodeId(node.parent))
300 }
301 }
302
303 pub fn add_edge(&mut self, from: NodeId, to: NodeId) -> Result<(), CycleError> {
307 if from == to {
309 return Err(CycleError { from, to });
310 }
311
312 if self.can_reach(to, from) {
315 return Err(CycleError { from, to });
316 }
317
318 self.fwd_adj[from.0 as usize].push(to);
320 self.rev_adj[to.0 as usize].push(from);
322
323 Ok(())
324 }
325
326 fn can_reach(&self, from: NodeId, to: NodeId) -> bool {
328 let mut visited = vec![false; self.nodes.len()];
329 let mut stack = vec![from];
330
331 while let Some(current) = stack.pop() {
332 if current == to {
333 return true;
334 }
335 let idx = current.0 as usize;
336 if idx >= self.nodes.len() || visited[idx] {
337 continue;
338 }
339 visited[idx] = true;
340
341 if self.nodes[idx].generation == 0 {
342 continue; }
344 for &dep in &self.fwd_adj[idx] {
345 if !visited[dep.0 as usize] {
346 stack.push(dep);
347 }
348 }
349 }
350 false
351 }
352
353 pub fn mark_changed(&mut self, id: NodeId, kind: InputKind, new_hash: u64) {
359 let idx = id.0 as usize;
360 if idx >= self.nodes.len() {
361 return;
362 }
363 let node = &mut self.nodes[idx];
364 if node.generation == 0 {
365 return; }
367
368 let old_hash = match kind {
369 InputKind::Constraint => &mut node.constraint_hash,
370 InputKind::Content => &mut node.content_hash,
371 InputKind::Style => &mut node.style_hash,
372 };
373
374 if *old_hash == new_hash {
375 return; }
377 *old_hash = new_hash;
378
379 node.dirty_gen = self.current_gen;
381 self.pending_dirty.push(id);
382 }
383
384 pub fn mark_dirty(&mut self, id: NodeId) {
386 let idx = id.0 as usize;
387 if idx >= self.nodes.len() {
388 return;
389 }
390 let node = &mut self.nodes[idx];
391 if node.generation == 0 {
392 return;
393 }
394 node.dirty_gen = self.current_gen;
395 self.pending_dirty.push(id);
396 }
397
398 pub fn propagate(&mut self) -> Vec<NodeId> {
404 if self.pending_dirty.is_empty() {
405 return Vec::new();
406 }
407
408 let mut queue = VecDeque::new();
410 let mut visited = vec![false; self.nodes.len()];
411
412 for &id in &self.pending_dirty {
413 let idx = id.0 as usize;
414 if idx < self.nodes.len() && !visited[idx] {
415 visited[idx] = true;
416 queue.push_back(id);
417 }
418 }
419 self.pending_dirty.clear();
420
421 while let Some(current) = queue.pop_front() {
422 let idx = current.0 as usize;
423 if self.nodes[idx].generation == 0 {
424 continue;
425 }
426
427 self.nodes[idx].dirty_gen = self.current_gen;
429
430 for i in 0..self.rev_adj[idx].len() {
432 let dependent = self.rev_adj[idx][i];
433 let dep_idx = dependent.0 as usize;
434 if dep_idx < self.nodes.len() && !visited[dep_idx] {
435 visited[dep_idx] = true;
436 queue.push_back(dependent);
437 }
438 }
439 }
440
441 self.collect_dirty_dfs_preorder()
443 }
444
445 fn collect_dirty_dfs_preorder(&self) -> Vec<NodeId> {
447 let mut roots = Vec::new();
449 for (i, node) in self.nodes.iter().enumerate() {
450 if node.generation == 0 || !node.is_dirty() {
451 continue;
452 }
453 let is_root = if node.parent == NodeId::NONE {
454 true
455 } else {
456 let parent = &self.nodes[node.parent as usize];
457 parent.generation == 0 || !parent.is_dirty()
458 };
459 if is_root {
460 roots.push(NodeId(i as u32));
461 }
462 }
463 roots.sort(); let mut result = Vec::new();
467 let mut visited = vec![false; self.nodes.len()];
468
469 for root in roots {
470 self.dfs_preorder(root, &mut result, &mut visited);
471 }
472 result
473 }
474
475 fn dfs_preorder(&self, id: NodeId, result: &mut Vec<NodeId>, visited: &mut [bool]) {
477 let idx = id.0 as usize;
478 if idx >= self.nodes.len() || visited[idx] {
479 return;
480 }
481 let node = &self.nodes[idx];
482 if node.generation == 0 || !node.is_dirty() {
483 return;
484 }
485 visited[idx] = true;
486 result.push(id);
487
488 let mut children: Vec<NodeId> = self.rev_adj[idx]
490 .iter()
491 .filter(|d| {
492 let di = d.0 as usize;
493 di < self.nodes.len()
494 && !visited[di]
495 && self.nodes[di].generation != 0
496 && self.nodes[di].is_dirty()
497 })
498 .copied()
499 .collect();
500 children.sort(); for child in children {
502 self.dfs_preorder(child, result, visited);
503 }
504 }
505
506 #[must_use]
508 pub fn is_dirty(&self, id: NodeId) -> bool {
509 self.nodes
510 .get(id.0 as usize)
511 .is_some_and(|n| n.generation != 0 && n.is_dirty())
512 }
513
514 pub fn clean(&mut self, id: NodeId) {
516 if let Some(node) = self.nodes.get_mut(id.0 as usize) {
517 node.generation = self.current_gen;
518 node.dirty_gen = 0;
519 }
520 }
521
522 pub fn clean_all(&mut self) {
524 self.current_gen = self.current_gen.wrapping_add(1);
525 if self.current_gen == 0 {
526 self.current_gen = 1; }
528 for node in &mut self.nodes {
529 if node.generation != 0 {
530 node.generation = self.current_gen;
531 node.dirty_gen = 0;
532 }
533 }
534 self.pending_dirty.clear();
535 }
536
537 #[must_use]
539 pub fn constraint_hash(&self, id: NodeId) -> Option<u64> {
540 self.nodes
541 .get(id.0 as usize)
542 .filter(|n| n.generation != 0)
543 .map(|n| n.constraint_hash)
544 }
545
546 #[must_use]
548 pub fn content_hash(&self, id: NodeId) -> Option<u64> {
549 self.nodes
550 .get(id.0 as usize)
551 .filter(|n| n.generation != 0)
552 .map(|n| n.content_hash)
553 }
554
555 #[must_use]
557 pub fn style_hash(&self, id: NodeId) -> Option<u64> {
558 self.nodes
559 .get(id.0 as usize)
560 .filter(|n| n.generation != 0)
561 .map(|n| n.style_hash)
562 }
563
564 pub fn dirty_nodes(&self) -> impl Iterator<Item = NodeId> + '_ {
566 self.nodes
567 .iter()
568 .enumerate()
569 .filter(|(_, n)| n.generation != 0 && n.is_dirty())
570 .map(|(i, _)| NodeId(i as u32))
571 }
572
573 #[must_use]
575 pub fn dirty_count(&self) -> usize {
576 self.nodes
577 .iter()
578 .filter(|n| n.generation != 0 && n.is_dirty())
579 .count()
580 }
581
582 #[must_use]
584 pub fn dependencies(&self, id: NodeId) -> &[NodeId] {
585 let idx = id.0 as usize;
586 if idx >= self.nodes.len() || self.nodes[idx].generation == 0 {
587 return &[];
588 }
589 &self.fwd_adj[idx]
590 }
591
592 #[must_use]
594 pub fn dependents(&self, id: NodeId) -> &[NodeId] {
595 let idx = id.0 as usize;
596 if idx >= self.nodes.len() || self.nodes[idx].generation == 0 {
597 return &[];
598 }
599 &self.rev_adj[idx]
600 }
601
602 pub fn invalidate_all(&mut self) {
605 for (i, node) in self.nodes.iter_mut().enumerate() {
606 if node.generation != 0 {
607 node.dirty_gen = self.current_gen;
608 self.pending_dirty.push(NodeId(i as u32));
609 }
610 }
611 }
612}
613
614impl Default for DepGraph {
615 fn default() -> Self {
616 Self::new()
617 }
618}
619
620#[cfg(test)]
625mod tests {
626 use super::*;
627
628 #[test]
629 fn add_node_returns_sequential_ids() {
630 let mut g = DepGraph::new();
631 let a = g.add_node();
632 let b = g.add_node();
633 let c = g.add_node();
634 assert_eq!(a.raw(), 0);
635 assert_eq!(b.raw(), 1);
636 assert_eq!(c.raw(), 2);
637 assert_eq!(g.node_count(), 3);
638 }
639
640 #[test]
641 fn remove_node_recycles_slot() {
642 let mut g = DepGraph::new();
643 let a = g.add_node();
644 let _b = g.add_node();
645 g.remove_node(a);
646 assert_eq!(g.node_count(), 1);
647 let c = g.add_node();
648 assert_eq!(c.raw(), 0); assert_eq!(g.node_count(), 2);
650 }
651
652 #[test]
653 fn add_edge_creates_dependency() {
654 let mut g = DepGraph::new();
655 let a = g.add_node();
656 let b = g.add_node();
657 g.add_edge(a, b).unwrap();
658 assert_eq!(g.dependencies(a), &[b]);
659 assert_eq!(g.dependents(b), &[a]);
660 assert_eq!(g.edge_count(), 1);
661 }
662
663 #[test]
664 fn self_loop_detected() {
665 let mut g = DepGraph::new();
666 let a = g.add_node();
667 let err = g.add_edge(a, a).unwrap_err();
668 assert_eq!(err, CycleError { from: a, to: a });
669 }
670
671 #[test]
672 fn two_node_cycle_detected() {
673 let mut g = DepGraph::new();
674 let a = g.add_node();
675 let b = g.add_node();
676 g.add_edge(a, b).unwrap();
677 let err = g.add_edge(b, a).unwrap_err();
678 assert_eq!(err, CycleError { from: b, to: a });
679 }
680
681 #[test]
682 fn three_node_cycle_detected() {
683 let mut g = DepGraph::new();
684 let a = g.add_node();
685 let b = g.add_node();
686 let c = g.add_node();
687 g.add_edge(a, b).unwrap();
688 g.add_edge(b, c).unwrap();
689 let err = g.add_edge(c, a).unwrap_err();
690 assert_eq!(err, CycleError { from: c, to: a });
691 }
692
693 #[test]
694 fn dag_allows_diamond() {
695 let mut g = DepGraph::new();
697 let a = g.add_node();
698 let b = g.add_node();
699 let c = g.add_node();
700 let d = g.add_node();
701 g.add_edge(a, b).unwrap();
702 g.add_edge(a, c).unwrap();
703 g.add_edge(b, d).unwrap();
704 g.add_edge(c, d).unwrap();
705 assert_eq!(g.edge_count(), 4);
706 }
707
708 #[test]
709 fn mark_changed_deduplicates() {
710 let mut g = DepGraph::new();
711 let a = g.add_node();
712 g.mark_changed(a, InputKind::Constraint, 42);
713 assert!(g.is_dirty(a));
714
715 g.clean(a);
717 g.mark_changed(a, InputKind::Constraint, 42);
718 assert!(!g.is_dirty(a)); }
720
721 #[test]
722 fn mark_changed_different_hash() {
723 let mut g = DepGraph::new();
724 let a = g.add_node();
725 g.mark_changed(a, InputKind::Constraint, 42);
726 g.clean(a);
727 g.mark_changed(a, InputKind::Constraint, 99); assert!(g.is_dirty(a));
729 }
730
731 #[test]
732 fn propagate_single_node() {
733 let mut g = DepGraph::new();
734 let a = g.add_node();
735 g.mark_dirty(a);
736 let dirty = g.propagate();
737 assert_eq!(dirty, vec![a]);
738 }
739
740 #[test]
741 fn propagate_parent_to_child() {
742 let mut g = DepGraph::new();
743 let parent = g.add_node();
744 let child = g.add_node();
745 g.add_edge(child, parent).unwrap(); g.set_parent(child, parent);
747
748 g.mark_dirty(parent);
749 let dirty = g.propagate();
750 assert!(dirty.contains(&parent));
751 assert!(dirty.contains(&child));
752 }
753
754 #[test]
755 fn propagate_chain() {
756 let mut g = DepGraph::new();
758 let a = g.add_node();
759 let b = g.add_node();
760 let c = g.add_node();
761 g.add_edge(b, a).unwrap(); g.add_edge(c, b).unwrap(); g.set_parent(b, a);
764 g.set_parent(c, b);
765
766 g.mark_dirty(a);
767 let dirty = g.propagate();
768 assert_eq!(dirty.len(), 3);
769 assert!(dirty.contains(&a));
770 assert!(dirty.contains(&b));
771 assert!(dirty.contains(&c));
772 }
773
774 #[test]
775 fn propagate_only_affected_subtree() {
776 let mut g = DepGraph::new();
778 let a = g.add_node();
779 let b = g.add_node();
780 let c = g.add_node();
781 let d = g.add_node();
782 g.add_edge(b, a).unwrap();
783 g.add_edge(c, a).unwrap();
784
785 g.mark_dirty(a);
786 let dirty = g.propagate();
787 assert!(dirty.contains(&a));
788 assert!(dirty.contains(&b));
789 assert!(dirty.contains(&c));
790 assert!(!dirty.contains(&d));
791 }
792
793 #[test]
794 fn propagate_diamond_deduplicates() {
795 let mut g = DepGraph::new();
797 let a = g.add_node();
798 let b = g.add_node();
799 let c = g.add_node();
800 let d = g.add_node();
801 g.add_edge(b, a).unwrap();
802 g.add_edge(c, a).unwrap();
803 g.add_edge(d, b).unwrap();
804 g.add_edge(d, c).unwrap();
805
806 g.mark_dirty(a);
807 let dirty = g.propagate();
808 assert_eq!(dirty.len(), 4);
809 let unique: std::collections::HashSet<_> = dirty.iter().collect();
811 assert_eq!(unique.len(), 4);
812 }
813
814 #[test]
815 fn clean_all_resets() {
816 let mut g = DepGraph::new();
817 let a = g.add_node();
818 let b = g.add_node();
819 g.add_edge(b, a).unwrap();
820 g.mark_dirty(a);
821 g.propagate();
822 assert!(g.is_dirty(a));
823 assert!(g.is_dirty(b));
824
825 g.clean_all();
826 assert!(!g.is_dirty(a));
827 assert!(!g.is_dirty(b));
828 assert_eq!(g.dirty_count(), 0);
829 }
830
831 #[test]
832 fn invalidate_all_dirties_everything() {
833 let mut g = DepGraph::new();
834 let a = g.add_node();
835 let b = g.add_node();
836 let c = g.add_node();
837
838 g.invalidate_all();
839 let dirty = g.propagate();
840 assert_eq!(dirty.len(), 3);
841 assert!(dirty.contains(&a));
842 assert!(dirty.contains(&b));
843 assert!(dirty.contains(&c));
844 }
845
846 #[test]
847 fn parent_child_relationship() {
848 let mut g = DepGraph::new();
849 let a = g.add_node();
850 let b = g.add_node();
851 assert_eq!(g.parent(a), None);
852 g.set_parent(b, a);
853 assert_eq!(g.parent(b), Some(a));
854 }
855
856 #[test]
857 fn input_hashes_stored_independently() {
858 let mut g = DepGraph::new();
859 let a = g.add_node();
860 g.mark_changed(a, InputKind::Constraint, 1);
861 g.mark_changed(a, InputKind::Content, 2);
862 g.mark_changed(a, InputKind::Style, 3);
863
864 assert_eq!(g.constraint_hash(a), Some(1));
865 assert_eq!(g.content_hash(a), Some(2));
866 assert_eq!(g.style_hash(a), Some(3));
867 }
868
869 #[test]
870 fn dead_node_is_not_dirty() {
871 let mut g = DepGraph::new();
872 let a = g.add_node();
873 g.mark_dirty(a);
874 g.remove_node(a);
875 assert!(!g.is_dirty(a));
876 }
877
878 #[test]
879 fn propagate_skips_dead_nodes() {
880 let mut g = DepGraph::new();
881 let a = g.add_node();
882 let b = g.add_node();
883 let c = g.add_node();
884 g.add_edge(b, a).unwrap();
885 g.add_edge(c, b).unwrap();
886
887 g.remove_node(b);
889 g.mark_dirty(a);
890 let dirty = g.propagate();
891 assert!(dirty.contains(&a));
893 assert!(!dirty.contains(&c));
894 }
895
896 #[test]
897 fn propagate_empty_returns_empty() {
898 let mut g = DepGraph::new();
899 let _a = g.add_node();
900 let dirty = g.propagate();
901 assert!(dirty.is_empty());
902 }
903
904 #[test]
905 fn large_tree_propagation() {
906 let mut g = DepGraph::with_capacity(1000, 1000);
908 let root = g.add_node();
909 let mut leaves = Vec::new();
910
911 for _ in 0..10 {
912 let child = g.add_node();
913 g.add_edge(child, root).unwrap();
914 g.set_parent(child, root);
915
916 for _ in 0..10 {
917 let grandchild = g.add_node();
918 g.add_edge(grandchild, child).unwrap();
919 g.set_parent(grandchild, child);
920 leaves.push(grandchild);
921 }
922 }
923 assert_eq!(g.node_count(), 111); g.mark_dirty(root);
927 let dirty = g.propagate();
928 assert_eq!(dirty.len(), 111);
929
930 g.clean_all();
932 g.mark_dirty(leaves[42]);
933 let dirty = g.propagate();
934 assert_eq!(dirty.len(), 1);
935 assert_eq!(dirty[0], leaves[42]);
936 }
937
938 #[test]
939 fn node_size_under_64_bytes() {
940 assert!(
941 std::mem::size_of::<DepNode>() <= 64,
942 "DepNode is {} bytes, exceeds 64-byte budget",
943 std::mem::size_of::<DepNode>(),
944 );
945 }
946
947 #[test]
948 fn node_size_exactly_40_bytes() {
949 assert_eq!(
951 std::mem::size_of::<DepNode>(),
952 40,
953 "DepNode should be 40 bytes, got {}",
954 std::mem::size_of::<DepNode>(),
955 );
956 }
957
958 #[test]
959 fn multiple_input_changes_single_propagation() {
960 let mut g = DepGraph::new();
961 let a = g.add_node();
962 let b = g.add_node();
963 g.add_edge(b, a).unwrap();
964
965 g.mark_changed(a, InputKind::Constraint, 10);
967 g.mark_changed(a, InputKind::Content, 20);
968 let dirty = g.propagate();
969 assert_eq!(dirty.len(), 2); }
971
972 #[test]
973 fn propagate_dfs_preorder() {
974 let mut g = DepGraph::new();
976 let r = g.add_node(); let a = g.add_node(); let b = g.add_node(); let c = g.add_node(); g.add_edge(a, r).unwrap();
981 g.add_edge(b, a).unwrap();
982 g.add_edge(c, a).unwrap();
983 g.set_parent(a, r);
984 g.set_parent(b, a);
985 g.set_parent(c, a);
986
987 g.mark_dirty(r);
988 let dirty = g.propagate();
989 assert_eq!(dirty.len(), 4);
990 assert_eq!(dirty[0], r);
992 assert_eq!(dirty[1], a);
993 assert_eq!(dirty[2], b);
995 assert_eq!(dirty[3], c);
996 }
997
998 #[test]
999 fn dirty_count_accurate() {
1000 let mut g = DepGraph::new();
1001 let a = g.add_node();
1002 let b = g.add_node();
1003 let c = g.add_node();
1004 assert_eq!(g.dirty_count(), 0);
1005
1006 g.mark_dirty(a);
1007 g.mark_dirty(b);
1008 g.propagate();
1009 assert_eq!(g.dirty_count(), 2);
1010
1011 g.clean(a);
1012 assert_eq!(g.dirty_count(), 1);
1013
1014 g.clean_all();
1015 assert_eq!(g.dirty_count(), 0);
1016
1017 let _ = c;
1019 }
1020
1021 #[test]
1022 fn dependencies_and_dependents_api() {
1023 let mut g = DepGraph::new();
1024 let a = g.add_node();
1025 let b = g.add_node();
1026 let c = g.add_node();
1027 g.add_edge(b, a).unwrap(); g.add_edge(c, a).unwrap(); assert_eq!(g.dependencies(b), &[a]);
1031 assert_eq!(g.dependencies(c), &[a]);
1032 assert_eq!(g.dependents(a).len(), 2);
1033 }
1034}