1use crate::details::PackageDetails;
5use guppy::{
6 graph::{
7 feature::{FeatureGraph, FeatureId, FeatureMetadata, FeatureQuery, FeatureSet},
8 DependencyDirection, DependencyReq, PackageGraph, PackageLink, PackageLinkPtrs,
9 PackageMetadata, PackageQuery, PackageSet,
10 },
11 platform::PlatformSpec,
12 DependencyKind, Error, PackageId,
13};
14use pretty_assertions::assert_eq;
15use std::{
16 collections::{BTreeSet, HashSet},
17 fmt,
18 hash::Hash,
19 iter,
20};
21
22fn __from_metadata<'a>(link: &PackageLink<'a>) -> PackageMetadata<'a> {
23 link.from()
24}
25fn __to_metadata<'a>(link: &PackageLink<'a>) -> PackageMetadata<'a> {
26 link.to()
27}
28type LinkToMetadata<'a> = fn(&PackageLink<'a>) -> PackageMetadata<'a>;
29
30#[derive(Clone, Copy)]
34pub struct DirectionDesc<'a> {
35 direction_desc: &'static str,
36 known_desc: &'static str,
37 variable_desc: &'static str,
38 known_metadata: LinkToMetadata<'a>,
39 variable_metadata: LinkToMetadata<'a>,
40}
41
42impl<'a> DirectionDesc<'a> {
43 fn new(direction: DependencyDirection) -> Self {
44 match direction {
45 DependencyDirection::Forward => Self::forward(),
46 DependencyDirection::Reverse => Self::reverse(),
47 }
48 }
49
50 fn forward() -> Self {
51 Self {
52 direction_desc: "forward",
53 known_desc: "from",
54 variable_desc: "to",
55 known_metadata: __from_metadata as LinkToMetadata<'a>,
56 variable_metadata: __to_metadata as LinkToMetadata<'a>,
57 }
58 }
59
60 fn reverse() -> Self {
61 Self {
62 direction_desc: "reverse",
63 known_desc: "to",
64 variable_desc: "from",
65 known_metadata: __to_metadata as LinkToMetadata<'a>,
66 variable_metadata: __from_metadata as LinkToMetadata<'a>,
67 }
68 }
69
70 fn known_metadata(&self, dep: &PackageLink<'a>) -> PackageMetadata<'a> {
71 (self.known_metadata)(dep)
72 }
73
74 fn variable_metadata(&self, dep: &PackageLink<'a>) -> PackageMetadata<'a> {
75 (self.variable_metadata)(dep)
76 }
77}
78
79impl From<DependencyDirection> for DirectionDesc<'_> {
80 fn from(direction: DependencyDirection) -> Self {
81 Self::new(direction)
82 }
83}
84
85pub(crate) fn assert_deps_internal(
86 graph: &PackageGraph,
87 direction: DependencyDirection,
88 known_details: &PackageDetails,
89 msg: &str,
90) {
91 let desc = DirectionDesc::new(direction);
92
93 let expected_dep_ids: Vec<_> = known_details
95 .deps(direction)
96 .unwrap_or_else(|| {
97 panic!(
98 "{}: {} dependencies must be present",
99 msg, desc.direction_desc
100 )
101 })
102 .iter()
103 .map(|(dep_name, id)| (*dep_name, dep_name.replace('-', "_"), id))
104 .collect();
105 let actual_deps: Vec<_> = graph
106 .metadata(known_details.id())
107 .unwrap_or_else(|err| panic!("{}: {}", msg, err))
108 .direct_links_directed(direction)
109 .collect();
110 let mut actual_dep_ids: Vec<_> = actual_deps
111 .iter()
112 .map(|link| {
113 (
114 link.dep_name(),
115 link.resolved_name().to_string(),
116 desc.variable_metadata(link).id(),
117 )
118 })
119 .collect();
120 actual_dep_ids.sort();
121 assert_eq!(
122 expected_dep_ids, actual_dep_ids,
123 "{}: expected {} dependencies",
124 msg, desc.direction_desc,
125 );
126
127 for (_, _, dep_id) in &actual_dep_ids {
128 graph.assert_depends_on(known_details.id(), dep_id, direction, msg);
130 graph.assert_directly_depends_on(known_details.id(), dep_id, direction, msg);
131 }
132
133 let known_msg = format!(
135 "{}: {} dependency edge {} this package",
136 msg, desc.direction_desc, desc.known_desc
137 );
138 for actual_dep in &actual_deps {
139 known_details.assert_metadata(desc.known_metadata(actual_dep), &known_msg);
140 }
142}
143
144pub(crate) fn assert_transitive_deps_internal(
145 graph: &PackageGraph,
146 direction: DependencyDirection,
147 known_details: &PackageDetails,
148 msg: &str,
149) {
150 let desc = DirectionDesc::new(direction);
151
152 let expected_dep_ids = known_details.transitive_deps(direction).unwrap_or_else(|| {
153 panic!(
154 "{}: {} transitive dependencies must be present",
155 msg, desc.direction_desc
156 )
157 });
158
159 let query = graph
160 .query_directed(iter::once(known_details.id()), direction)
161 .unwrap_or_else(|err| {
162 panic!(
163 "{}: {} transitive dep query failed: {}",
164 msg, desc.direction_desc, err
165 )
166 });
167 let package_set = query.resolve();
168
169 let package_ids = package_set.package_ids(direction);
170 let mut actual_dep_ids: Vec<_> = package_ids.collect();
171 actual_dep_ids.sort();
172
173 let actual_deps: Vec<_> = package_set.links(direction).collect();
174 let actual_ptrs = dep_link_ptrs(actual_deps.iter().copied());
175
176 let ids_from_links_set: BTreeSet<_> = actual_deps
178 .iter()
179 .flat_map(|link| vec![link.from().id(), link.to().id()])
180 .collect();
181 let ids_from_links: Vec<_> = ids_from_links_set.iter().copied().collect();
182
183 assert_eq!(
184 expected_dep_ids,
185 actual_dep_ids.as_slice(),
186 "{}: expected {} transitive dependency IDs",
187 msg,
188 desc.direction_desc
189 );
190 assert_eq!(
191 expected_dep_ids,
192 ids_from_links.as_slice(),
193 "{}: expected {} transitive dependency infos",
194 msg,
195 desc.direction_desc
196 );
197
198 assert_link_order(
201 actual_deps,
202 package_set.root_ids(direction),
203 desc,
204 &format!("{}: actual link order", msg),
205 );
206
207 let opposite = direction.opposite();
209 let opposite_desc = DirectionDesc::new(opposite);
210 let opposite_deps: Vec<_> = package_set.links(opposite).collect();
211 let opposite_ptrs = dep_link_ptrs(opposite_deps.iter().copied());
212
213 assert_eq!(
215 actual_ptrs, opposite_ptrs,
216 "{}: actual and opposite links should return the same pointer triples",
217 msg,
218 );
219
220 assert_link_order(
221 opposite_deps,
222 package_set.root_ids(opposite),
223 opposite_desc,
224 &format!("{}: opposite link order", msg),
225 );
226
227 for dep_id in expected_dep_ids {
228 graph.assert_depends_on(known_details.id(), dep_id, direction, msg);
230
231 let dep_actual_dep_ids: BTreeSet<_> = graph
233 .query_directed(iter::once(dep_id), direction)
234 .unwrap_or_else(|err| {
235 panic!(
236 "{}: {} transitive dep id query failed for dependency '{}': {}",
237 msg, desc.direction_desc, dep_id, err
238 )
239 })
240 .resolve()
241 .package_ids(direction)
242 .collect();
243 let difference: Vec<_> = dep_actual_dep_ids.difference(&ids_from_links_set).collect();
245 assert!(
246 difference.is_empty(),
247 "{}: unexpected extra {} transitive dependency IDs for dep '{}': {:?}",
248 msg,
249 desc.direction_desc,
250 dep_id,
251 difference
252 );
253
254 let dep_ids_from_links: BTreeSet<_> = graph
255 .query_directed(iter::once(dep_id), direction)
256 .unwrap_or_else(|err| {
257 panic!(
258 "{}: {} transitive dep query failed for dependency '{}': {}",
259 msg, desc.direction_desc, dep_id, err
260 )
261 })
262 .resolve()
263 .links(direction)
264 .flat_map(|dep| vec![dep.from().id(), dep.to().id()])
265 .collect();
266 let difference: Vec<_> = dep_ids_from_links.difference(&ids_from_links_set).collect();
268 assert!(
269 difference.is_empty(),
270 "{}: unexpected extra {} transitive dependencies for dep '{}': {:?}",
271 msg,
272 desc.direction_desc,
273 dep_id,
274 difference
275 );
276 }
277}
278
279pub(crate) fn assert_topo_ids(graph: &PackageGraph, direction: DependencyDirection, msg: &str) {
280 let all_set = graph.resolve_all();
281 let topo_ids = all_set.package_ids(direction);
282 assert_eq!(
283 topo_ids.len(),
284 graph.package_count(),
285 "{}: topo sort returns all packages",
286 msg
287 );
288
289 graph.assert_topo_order(topo_ids, direction, msg);
291}
292
293pub(crate) fn assert_topo_metadatas(
294 graph: &PackageGraph,
295 direction: DependencyDirection,
296 msg: &str,
297) {
298 let all_set = graph.resolve_all();
299 let topo_metadatas = all_set.packages(direction);
300 assert_eq!(
301 topo_metadatas.len(),
302 graph.package_count(),
303 "{}: topo sort returns all packages",
304 msg
305 );
306 let topo_ids = topo_metadatas.map(|metadata| metadata.id());
307
308 graph.assert_topo_order(topo_ids, direction, msg);
310}
311
312pub(crate) fn assert_all_links(graph: &PackageGraph, direction: DependencyDirection, msg: &str) {
313 let desc = DirectionDesc::new(direction);
314 let all_links: Vec<_> = graph.resolve_all().links(direction).collect();
315 assert_eq!(
316 all_links.len(),
317 graph.link_count(),
318 "{}: all links should be returned",
319 msg
320 );
321
322 for link in &all_links {
324 for dep_kind in &[
325 DependencyKind::Normal,
326 DependencyKind::Build,
327 DependencyKind::Development,
328 ] {
329 assert_enabled_status_is_known(
330 link.req_for_kind(*dep_kind),
331 &format!(
332 "{}: {} -> {} ({})",
333 msg,
334 link.from().id(),
335 link.to().id(),
336 dep_kind,
337 ),
338 );
339 }
340 }
341
342 assert_link_order(
344 all_links,
345 graph.resolve_all().root_ids(direction),
346 desc,
347 msg,
348 );
349}
350
351fn assert_enabled_status_is_known(req: DependencyReq<'_>, msg: &str) {
352 let current_platform = PlatformSpec::build_target().expect("current platform is known");
353 assert!(
354 req.status().enabled_on(¤t_platform).is_known(),
355 "{}: enabled status known for current platform",
356 msg
357 );
358 assert!(
359 req.default_features()
360 .enabled_on(¤t_platform)
361 .is_known(),
362 "{}: default feature status known for current platform",
363 msg
364 );
365 for feature in req.features() {
366 assert!(
367 req.feature_status(feature)
368 .enabled_on(¤t_platform)
369 .is_known(),
370 "{}: for feature '{}', status known for current platform",
371 msg,
372 feature
373 );
374 }
375}
376
377pub trait GraphAssert<'g>: Copy + fmt::Debug {
378 type Id: Copy + Eq + Hash + fmt::Debug;
379 type Metadata: GraphMetadata<'g, Id = Self::Id>;
380 type Query: GraphQuery<'g, Id = Self::Id, Set = Self::Set>;
381 type Set: GraphSet<'g, Id = Self::Id, Metadata = Self::Metadata>;
382 const NAME: &'static str;
383
384 fn depends_on(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error>;
387
388 fn directly_depends_on(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error>;
389
390 fn is_cyclic(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error>;
391
392 fn query(
393 &self,
394 initials: impl IntoIterator<Item = Self::Id>,
395 direction: DependencyDirection,
396 ) -> Self::Query;
397
398 fn resolve(&self, initials: &[Self::Id], direction: DependencyDirection) -> Self::Set {
399 self.query(initials.iter().copied(), direction).resolve()
400 }
401
402 fn ids(
403 &self,
404 initials: &[Self::Id],
405 query_direction: DependencyDirection,
406 iter_direction: DependencyDirection,
407 ) -> Vec<Self::Id> {
408 let package_set = self.resolve(initials, query_direction);
409 let resolve_len = package_set.len();
410 let ids = package_set.ids(iter_direction);
411 assert_eq!(resolve_len, ids.len(), "resolve.len() is correct");
412 ids
413 }
414
415 fn root_ids(
416 &self,
417 initials: &[Self::Id],
418 query_direction: DependencyDirection,
419 iter_direction: DependencyDirection,
420 ) -> Vec<Self::Id> {
421 self.resolve(initials, query_direction)
422 .root_ids(iter_direction)
423 }
424
425 fn root_metadatas(
426 &self,
427 initials: &[Self::Id],
428 query_direction: DependencyDirection,
429 iter_direction: DependencyDirection,
430 ) -> Vec<Self::Metadata> {
431 self.resolve(initials, query_direction)
432 .root_metadatas(iter_direction)
433 }
434
435 fn assert_topo_order(
436 &self,
437 topo_ids: impl IntoIterator<Item = Self::Id>,
438 direction: DependencyDirection,
439 msg: &str,
440 ) {
441 let topo_ids: Vec<_> = topo_ids.into_iter().collect();
442 for (idx, earlier_package) in topo_ids.iter().enumerate() {
443 for later_package in topo_ids.iter().skip(idx + 1).take(20) {
448 self.assert_not_depends_on(*later_package, *earlier_package, direction, msg);
449 }
450 }
451 }
452
453 fn assert_depends_on_any(
454 &self,
455 source_ids: &[Self::Id],
456 query_id: Self::Id,
457 direction: DependencyDirection,
458 msg: &str,
459 ) {
460 let any_depends_on = source_ids.iter().any(|source_id| match direction {
461 DependencyDirection::Forward => self.depends_on(*source_id, query_id).unwrap(),
462 DependencyDirection::Reverse => self.depends_on(query_id, *source_id).unwrap(),
463 });
464 match direction {
465 DependencyDirection::Forward => {
466 assert!(
467 any_depends_on,
468 "{}: {} '{:?}' should be a dependency of any of '{:?}'",
469 msg,
470 Self::NAME,
471 query_id,
472 source_ids
473 );
474 }
475 DependencyDirection::Reverse => {
476 assert!(
477 any_depends_on,
478 "{}: {} '{:?}' should depend on any of '{:?}'",
479 msg,
480 Self::NAME,
481 query_id,
482 source_ids
483 );
484 }
485 }
486 }
487
488 fn assert_depends_on(
489 &self,
490 a_id: Self::Id,
491 b_id: Self::Id,
492 direction: DependencyDirection,
493 msg: &str,
494 ) {
495 match direction {
496 DependencyDirection::Forward => assert!(
497 self.depends_on(a_id, b_id).unwrap(),
498 "{}: {} '{:?}' should depend on '{:?}'",
499 msg,
500 Self::NAME,
501 a_id,
502 b_id,
503 ),
504 DependencyDirection::Reverse => assert!(
505 self.depends_on(b_id, a_id).unwrap(),
506 "{}: {} '{:?}' should be a dependency of '{:?}'",
507 msg,
508 Self::NAME,
509 a_id,
510 b_id,
511 ),
512 }
513 }
514
515 fn assert_not_depends_on(
516 &self,
517 a_id: Self::Id,
518 b_id: Self::Id,
519 direction: DependencyDirection,
520 msg: &str,
521 ) {
522 if self.is_cyclic(a_id, b_id).unwrap() {
523 return;
526 }
527
528 match direction {
529 DependencyDirection::Forward => assert!(
530 !self.depends_on(a_id, b_id).unwrap(),
531 "{}: {} '{:?}' should not depend on '{:?}'",
532 msg,
533 Self::NAME,
534 a_id,
535 b_id,
536 ),
537 DependencyDirection::Reverse => assert!(
538 !self.depends_on(b_id, a_id).unwrap(),
539 "{}: {} '{:?}' should not be a dependency of '{:?}'",
540 msg,
541 Self::NAME,
542 a_id,
543 b_id,
544 ),
545 }
546 }
547
548 fn assert_directly_depends_on(
549 &self,
550 a_id: Self::Id,
551 b_id: Self::Id,
552 direction: DependencyDirection,
553 msg: &str,
554 ) {
555 match direction {
556 DependencyDirection::Forward => assert!(
557 self.directly_depends_on(a_id, b_id).unwrap(),
558 "{}: {} '{:?}' should directly depend on '{:?}'",
559 msg,
560 Self::NAME,
561 a_id,
562 b_id,
563 ),
564 DependencyDirection::Reverse => assert!(
565 self.directly_depends_on(b_id, a_id).unwrap(),
566 "{}: {} '{:?}' should be a direct dependency of '{:?}'",
567 msg,
568 Self::NAME,
569 a_id,
570 b_id,
571 ),
572 }
573 }
574}
575
576pub trait GraphMetadata<'g> {
577 type Id: Copy + Eq + Hash + fmt::Debug;
578 fn id(&self) -> Self::Id;
579}
580
581pub trait GraphQuery<'g> {
582 type Id: Copy + Eq + Hash + fmt::Debug;
583 type Set: GraphSet<'g, Id = Self::Id>;
584
585 fn direction(&self) -> DependencyDirection;
586
587 fn starts_from(&self, id: Self::Id) -> bool;
588
589 fn resolve(self) -> Self::Set;
590}
591
592pub trait GraphSet<'g>: Clone + fmt::Debug {
593 type Id: Copy + Eq + Hash + fmt::Debug;
594 type Metadata: GraphMetadata<'g, Id = Self::Id>;
595 fn len(&self) -> usize;
596
597 fn is_empty(&self) -> bool {
598 self.len() == 0
599 }
600
601 fn contains(&self, id: Self::Id) -> bool;
602
603 fn union(&self, other: &Self) -> Self;
604 fn intersection(&self, other: &Self) -> Self;
605 fn difference(&self, other: &Self) -> Self;
606 fn symmetric_difference(&self, other: &Self) -> Self;
607
608 fn ids(&self, direction: DependencyDirection) -> Vec<Self::Id>;
609 fn metadatas(&self, direction: DependencyDirection) -> Vec<Self::Metadata>;
610 fn root_ids(&self, direction: DependencyDirection) -> Vec<Self::Id>;
611 fn root_metadatas(&self, direction: DependencyDirection) -> Vec<Self::Metadata>;
612}
613
614impl<'g> GraphAssert<'g> for &'g PackageGraph {
615 type Id = &'g PackageId;
616 type Metadata = PackageMetadata<'g>;
617 type Query = PackageQuery<'g>;
618 type Set = PackageSet<'g>;
619 const NAME: &'static str = "package";
620
621 fn depends_on(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error> {
622 PackageGraph::depends_on(self, a_id, b_id)
623 }
624
625 fn directly_depends_on(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error> {
626 PackageGraph::directly_depends_on(self, a_id, b_id)
627 }
628
629 fn is_cyclic(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error> {
630 let cycles = self.cycles();
631 cycles.is_cyclic(a_id, b_id)
632 }
633
634 fn query(
635 &self,
636 initials: impl IntoIterator<Item = Self::Id>,
637 direction: DependencyDirection,
638 ) -> Self::Query {
639 self.query_directed(initials, direction)
640 .expect("valid initials")
641 }
642}
643
644impl<'g> GraphMetadata<'g> for PackageMetadata<'g> {
645 type Id = &'g PackageId;
646 fn id(&self) -> Self::Id {
647 PackageMetadata::id(self)
648 }
649}
650
651impl<'g> GraphQuery<'g> for PackageQuery<'g> {
652 type Id = &'g PackageId;
653 type Set = PackageSet<'g>;
654
655 fn direction(&self) -> DependencyDirection {
656 self.direction()
657 }
658
659 fn starts_from(&self, id: Self::Id) -> bool {
660 self.starts_from(id).expect("valid ID")
661 }
662
663 fn resolve(self) -> Self::Set {
664 self.resolve()
665 }
666}
667
668impl<'g> GraphSet<'g> for PackageSet<'g> {
669 type Id = &'g PackageId;
670 type Metadata = PackageMetadata<'g>;
671
672 fn len(&self) -> usize {
673 self.len()
674 }
675
676 fn contains(&self, id: Self::Id) -> bool {
677 self.contains(id).unwrap()
678 }
679
680 fn union(&self, other: &Self) -> Self {
681 self.union(other)
682 }
683
684 fn intersection(&self, other: &Self) -> Self {
685 self.intersection(other)
686 }
687
688 fn difference(&self, other: &Self) -> Self {
689 self.difference(other)
690 }
691
692 fn symmetric_difference(&self, other: &Self) -> Self {
693 self.symmetric_difference(other)
694 }
695
696 fn ids(&self, direction: DependencyDirection) -> Vec<Self::Id> {
697 self.package_ids(direction).collect()
698 }
699
700 fn metadatas(&self, direction: DependencyDirection) -> Vec<Self::Metadata> {
701 self.packages(direction).collect()
702 }
703
704 fn root_ids(&self, direction: DependencyDirection) -> Vec<Self::Id> {
705 Self::root_ids(self, direction).collect()
706 }
707
708 fn root_metadatas(&self, direction: DependencyDirection) -> Vec<Self::Metadata> {
709 Self::root_packages(self, direction).collect()
710 }
711}
712
713impl<'g> GraphAssert<'g> for FeatureGraph<'g> {
714 type Id = FeatureId<'g>;
715 type Metadata = FeatureMetadata<'g>;
716 type Query = FeatureQuery<'g>;
717 type Set = FeatureSet<'g>;
718 const NAME: &'static str = "feature";
719
720 fn depends_on(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error> {
721 FeatureGraph::depends_on(self, a_id, b_id)
722 }
723
724 fn directly_depends_on(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error> {
725 FeatureGraph::directly_depends_on(self, a_id, b_id)
726 }
727
728 fn is_cyclic(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error> {
729 let cycles = self.cycles();
730 cycles.is_cyclic(a_id, b_id)
731 }
732
733 fn query(
734 &self,
735 initials: impl IntoIterator<Item = Self::Id>,
736 direction: DependencyDirection,
737 ) -> Self::Query {
738 self.query_directed(initials, direction)
739 .expect("valid initials")
740 }
741}
742
743impl<'g> GraphMetadata<'g> for FeatureMetadata<'g> {
744 type Id = FeatureId<'g>;
745 fn id(&self) -> Self::Id {
746 self.feature_id()
747 }
748}
749
750impl<'g> GraphQuery<'g> for FeatureQuery<'g> {
751 type Id = FeatureId<'g>;
752 type Set = FeatureSet<'g>;
753
754 fn direction(&self) -> DependencyDirection {
755 self.direction()
756 }
757
758 fn starts_from(&self, id: Self::Id) -> bool {
759 self.starts_from(id).expect("valid feature ID")
760 }
761
762 fn resolve(self) -> Self::Set {
763 self.resolve()
764 }
765}
766
767impl<'g> GraphSet<'g> for FeatureSet<'g> {
768 type Id = FeatureId<'g>;
769 type Metadata = FeatureMetadata<'g>;
770
771 fn len(&self) -> usize {
772 self.len()
773 }
774
775 fn contains(&self, id: Self::Id) -> bool {
776 self.contains(id).unwrap()
777 }
778
779 fn union(&self, other: &Self) -> Self {
780 self.union(other)
781 }
782
783 fn intersection(&self, other: &Self) -> Self {
784 self.intersection(other)
785 }
786
787 fn difference(&self, other: &Self) -> Self {
788 self.difference(other)
789 }
790
791 fn symmetric_difference(&self, other: &Self) -> Self {
792 self.symmetric_difference(other)
793 }
794
795 fn ids(&self, direction: DependencyDirection) -> Vec<Self::Id> {
796 self.feature_ids(direction).collect()
797 }
798
799 fn metadatas(&self, direction: DependencyDirection) -> Vec<Self::Metadata> {
800 self.features(direction).collect()
801 }
802
803 fn root_ids(&self, direction: DependencyDirection) -> Vec<Self::Id> {
804 Self::root_ids(self, direction).collect()
805 }
806
807 fn root_metadatas(&self, direction: DependencyDirection) -> Vec<Self::Metadata> {
808 Self::root_features(self, direction).collect()
809 }
810}
811
812pub fn assert_link_order<'g>(
820 links: impl IntoIterator<Item = PackageLink<'g>>,
821 initial: impl IntoIterator<Item = &'g PackageId>,
822 desc: impl Into<DirectionDesc<'g>>,
823 msg: &str,
824) {
825 let desc = desc.into();
826
827 let mut variable_seen: HashSet<_> = initial.into_iter().collect();
829
830 for link in links {
831 let known_id = desc.known_metadata(&link).id();
832 let variable_id = desc.variable_metadata(&link).id();
833
834 variable_seen.insert(variable_id);
835 assert!(
836 variable_seen.contains(&known_id),
837 "{}: for package '{}': unexpected link {} package seen before any links {} package",
838 msg,
839 &known_id,
840 desc.known_desc,
841 desc.variable_desc,
842 );
843 }
844}
845
846fn dep_link_ptrs<'g>(dep_links: impl IntoIterator<Item = PackageLink<'g>>) -> Vec<PackageLinkPtrs> {
847 let mut triples: Vec<_> = dep_links
848 .into_iter()
849 .map(|link| link.as_inner_ptrs())
850 .collect();
851 triples.sort();
852 triples
853}