1use crate::details::PackageDetails;
5use guppy::{
6 DependencyKind, Error, PackageId,
7 graph::{
8 DependencyDirection, DependencyReq, PackageGraph, PackageLink, PackageLinkPtrs,
9 PackageMetadata, PackageQuery, PackageSet,
10 feature::{FeatureGraph, FeatureId, FeatureMetadata, FeatureQuery, FeatureSet},
11 },
12 platform::PlatformSpec,
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!("{msg}: actual link order"),
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!("{msg}: opposite link order"),
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 "{msg}: enabled status known for current platform"
356 );
357 assert!(
358 req.default_features()
359 .enabled_on(¤t_platform)
360 .is_known(),
361 "{msg}: default feature status known for current platform"
362 );
363 for feature in req.features() {
364 assert!(
365 req.feature_status(feature)
366 .enabled_on(¤t_platform)
367 .is_known(),
368 "{msg}: for feature '{feature}', status known for current platform"
369 );
370 }
371}
372
373pub trait GraphAssert<'g>: Copy + fmt::Debug {
374 type Id: Copy + Eq + Hash + fmt::Debug;
375 type Metadata: GraphMetadata<'g, Id = Self::Id>;
376 type Query: GraphQuery<'g, Id = Self::Id, Set = Self::Set>;
377 type Set: GraphSet<'g, Id = Self::Id, Metadata = Self::Metadata>;
378 const NAME: &'static str;
379
380 fn depends_on(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error>;
383
384 fn directly_depends_on(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error>;
385
386 fn is_cyclic(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error>;
387
388 fn query(
389 &self,
390 initials: impl IntoIterator<Item = Self::Id>,
391 direction: DependencyDirection,
392 ) -> Self::Query;
393
394 fn resolve(&self, initials: &[Self::Id], direction: DependencyDirection) -> Self::Set {
395 self.query(initials.iter().copied(), direction).resolve()
396 }
397
398 fn ids(
399 &self,
400 initials: &[Self::Id],
401 query_direction: DependencyDirection,
402 iter_direction: DependencyDirection,
403 ) -> Vec<Self::Id> {
404 let package_set = self.resolve(initials, query_direction);
405 let resolve_len = package_set.len();
406 let ids = package_set.ids(iter_direction);
407 assert_eq!(resolve_len, ids.len(), "resolve.len() is correct");
408 ids
409 }
410
411 fn root_ids(
412 &self,
413 initials: &[Self::Id],
414 query_direction: DependencyDirection,
415 iter_direction: DependencyDirection,
416 ) -> Vec<Self::Id> {
417 self.resolve(initials, query_direction)
418 .root_ids(iter_direction)
419 }
420
421 fn root_metadatas(
422 &self,
423 initials: &[Self::Id],
424 query_direction: DependencyDirection,
425 iter_direction: DependencyDirection,
426 ) -> Vec<Self::Metadata> {
427 self.resolve(initials, query_direction)
428 .root_metadatas(iter_direction)
429 }
430
431 fn assert_topo_order(
432 &self,
433 topo_ids: impl IntoIterator<Item = Self::Id>,
434 direction: DependencyDirection,
435 msg: &str,
436 ) {
437 let topo_ids: Vec<_> = topo_ids.into_iter().collect();
438 for (idx, earlier_package) in topo_ids.iter().enumerate() {
439 for later_package in topo_ids.iter().skip(idx + 1).take(20) {
444 self.assert_not_depends_on(*later_package, *earlier_package, direction, msg);
445 }
446 }
447 }
448
449 fn assert_depends_on_any(
450 &self,
451 source_ids: &[Self::Id],
452 query_id: Self::Id,
453 direction: DependencyDirection,
454 msg: &str,
455 ) {
456 let any_depends_on = source_ids.iter().any(|source_id| match direction {
457 DependencyDirection::Forward => self.depends_on(*source_id, query_id).unwrap(),
458 DependencyDirection::Reverse => self.depends_on(query_id, *source_id).unwrap(),
459 });
460 match direction {
461 DependencyDirection::Forward => {
462 assert!(
463 any_depends_on,
464 "{}: {} '{:?}' should be a dependency of any of '{:?}'",
465 msg,
466 Self::NAME,
467 query_id,
468 source_ids
469 );
470 }
471 DependencyDirection::Reverse => {
472 assert!(
473 any_depends_on,
474 "{}: {} '{:?}' should depend on any of '{:?}'",
475 msg,
476 Self::NAME,
477 query_id,
478 source_ids
479 );
480 }
481 }
482 }
483
484 fn assert_depends_on(
485 &self,
486 a_id: Self::Id,
487 b_id: Self::Id,
488 direction: DependencyDirection,
489 msg: &str,
490 ) {
491 match direction {
492 DependencyDirection::Forward => assert!(
493 self.depends_on(a_id, b_id).unwrap(),
494 "{}: {} '{:?}' should depend on '{:?}'",
495 msg,
496 Self::NAME,
497 a_id,
498 b_id,
499 ),
500 DependencyDirection::Reverse => assert!(
501 self.depends_on(b_id, a_id).unwrap(),
502 "{}: {} '{:?}' should be a dependency of '{:?}'",
503 msg,
504 Self::NAME,
505 a_id,
506 b_id,
507 ),
508 }
509 }
510
511 fn assert_not_depends_on(
512 &self,
513 a_id: Self::Id,
514 b_id: Self::Id,
515 direction: DependencyDirection,
516 msg: &str,
517 ) {
518 if self.is_cyclic(a_id, b_id).unwrap() {
519 return;
522 }
523
524 match direction {
525 DependencyDirection::Forward => assert!(
526 !self.depends_on(a_id, b_id).unwrap(),
527 "{}: {} '{:?}' should not depend on '{:?}'",
528 msg,
529 Self::NAME,
530 a_id,
531 b_id,
532 ),
533 DependencyDirection::Reverse => assert!(
534 !self.depends_on(b_id, a_id).unwrap(),
535 "{}: {} '{:?}' should not be a dependency of '{:?}'",
536 msg,
537 Self::NAME,
538 a_id,
539 b_id,
540 ),
541 }
542 }
543
544 fn assert_directly_depends_on(
545 &self,
546 a_id: Self::Id,
547 b_id: Self::Id,
548 direction: DependencyDirection,
549 msg: &str,
550 ) {
551 match direction {
552 DependencyDirection::Forward => assert!(
553 self.directly_depends_on(a_id, b_id).unwrap(),
554 "{}: {} '{:?}' should directly depend on '{:?}'",
555 msg,
556 Self::NAME,
557 a_id,
558 b_id,
559 ),
560 DependencyDirection::Reverse => assert!(
561 self.directly_depends_on(b_id, a_id).unwrap(),
562 "{}: {} '{:?}' should be a direct dependency of '{:?}'",
563 msg,
564 Self::NAME,
565 a_id,
566 b_id,
567 ),
568 }
569 }
570}
571
572pub trait GraphMetadata<'g> {
573 type Id: Copy + Eq + Hash + fmt::Debug;
574 fn id(&self) -> Self::Id;
575}
576
577pub trait GraphQuery<'g> {
578 type Id: Copy + Eq + Hash + fmt::Debug;
579 type Set: GraphSet<'g, Id = Self::Id>;
580
581 fn direction(&self) -> DependencyDirection;
582
583 fn starts_from(&self, id: Self::Id) -> bool;
584
585 fn resolve(self) -> Self::Set;
586}
587
588pub trait GraphSet<'g>: Clone + fmt::Debug {
589 type Id: Copy + Eq + Hash + fmt::Debug;
590 type Metadata: GraphMetadata<'g, Id = Self::Id>;
591 fn len(&self) -> usize;
592
593 fn is_empty(&self) -> bool {
594 self.len() == 0
595 }
596
597 fn contains(&self, id: Self::Id) -> bool;
598
599 fn union(&self, other: &Self) -> Self;
600 fn intersection(&self, other: &Self) -> Self;
601 fn difference(&self, other: &Self) -> Self;
602 fn symmetric_difference(&self, other: &Self) -> Self;
603
604 fn ids(&self, direction: DependencyDirection) -> Vec<Self::Id>;
605 fn metadatas(&self, direction: DependencyDirection) -> Vec<Self::Metadata>;
606 fn root_ids(&self, direction: DependencyDirection) -> Vec<Self::Id>;
607 fn root_metadatas(&self, direction: DependencyDirection) -> Vec<Self::Metadata>;
608}
609
610impl<'g> GraphAssert<'g> for &'g PackageGraph {
611 type Id = &'g PackageId;
612 type Metadata = PackageMetadata<'g>;
613 type Query = PackageQuery<'g>;
614 type Set = PackageSet<'g>;
615 const NAME: &'static str = "package";
616
617 fn depends_on(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error> {
618 PackageGraph::depends_on(self, a_id, b_id)
619 }
620
621 fn directly_depends_on(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error> {
622 PackageGraph::directly_depends_on(self, a_id, b_id)
623 }
624
625 fn is_cyclic(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error> {
626 let cycles = self.cycles();
627 cycles.is_cyclic(a_id, b_id)
628 }
629
630 fn query(
631 &self,
632 initials: impl IntoIterator<Item = Self::Id>,
633 direction: DependencyDirection,
634 ) -> Self::Query {
635 self.query_directed(initials, direction)
636 .expect("valid initials")
637 }
638}
639
640impl<'g> GraphMetadata<'g> for PackageMetadata<'g> {
641 type Id = &'g PackageId;
642 fn id(&self) -> Self::Id {
643 PackageMetadata::id(self)
644 }
645}
646
647impl<'g> GraphQuery<'g> for PackageQuery<'g> {
648 type Id = &'g PackageId;
649 type Set = PackageSet<'g>;
650
651 fn direction(&self) -> DependencyDirection {
652 self.direction()
653 }
654
655 fn starts_from(&self, id: Self::Id) -> bool {
656 self.starts_from(id).expect("valid ID")
657 }
658
659 fn resolve(self) -> Self::Set {
660 self.resolve()
661 }
662}
663
664impl<'g> GraphSet<'g> for PackageSet<'g> {
665 type Id = &'g PackageId;
666 type Metadata = PackageMetadata<'g>;
667
668 fn len(&self) -> usize {
669 self.len()
670 }
671
672 fn contains(&self, id: Self::Id) -> bool {
673 self.contains(id).unwrap()
674 }
675
676 fn union(&self, other: &Self) -> Self {
677 self.union(other)
678 }
679
680 fn intersection(&self, other: &Self) -> Self {
681 self.intersection(other)
682 }
683
684 fn difference(&self, other: &Self) -> Self {
685 self.difference(other)
686 }
687
688 fn symmetric_difference(&self, other: &Self) -> Self {
689 self.symmetric_difference(other)
690 }
691
692 fn ids(&self, direction: DependencyDirection) -> Vec<Self::Id> {
693 self.package_ids(direction).collect()
694 }
695
696 fn metadatas(&self, direction: DependencyDirection) -> Vec<Self::Metadata> {
697 self.packages(direction).collect()
698 }
699
700 fn root_ids(&self, direction: DependencyDirection) -> Vec<Self::Id> {
701 Self::root_ids(self, direction).collect()
702 }
703
704 fn root_metadatas(&self, direction: DependencyDirection) -> Vec<Self::Metadata> {
705 Self::root_packages(self, direction).collect()
706 }
707}
708
709impl<'g> GraphAssert<'g> for FeatureGraph<'g> {
710 type Id = FeatureId<'g>;
711 type Metadata = FeatureMetadata<'g>;
712 type Query = FeatureQuery<'g>;
713 type Set = FeatureSet<'g>;
714 const NAME: &'static str = "feature";
715
716 fn depends_on(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error> {
717 FeatureGraph::depends_on(self, a_id, b_id)
718 }
719
720 fn directly_depends_on(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error> {
721 FeatureGraph::directly_depends_on(self, a_id, b_id)
722 }
723
724 fn is_cyclic(&self, a_id: Self::Id, b_id: Self::Id) -> Result<bool, Error> {
725 let cycles = self.cycles();
726 cycles.is_cyclic(a_id, b_id)
727 }
728
729 fn query(
730 &self,
731 initials: impl IntoIterator<Item = Self::Id>,
732 direction: DependencyDirection,
733 ) -> Self::Query {
734 self.query_directed(initials, direction)
735 .expect("valid initials")
736 }
737}
738
739impl<'g> GraphMetadata<'g> for FeatureMetadata<'g> {
740 type Id = FeatureId<'g>;
741 fn id(&self) -> Self::Id {
742 self.feature_id()
743 }
744}
745
746impl<'g> GraphQuery<'g> for FeatureQuery<'g> {
747 type Id = FeatureId<'g>;
748 type Set = FeatureSet<'g>;
749
750 fn direction(&self) -> DependencyDirection {
751 self.direction()
752 }
753
754 fn starts_from(&self, id: Self::Id) -> bool {
755 self.starts_from(id).expect("valid feature ID")
756 }
757
758 fn resolve(self) -> Self::Set {
759 self.resolve()
760 }
761}
762
763impl<'g> GraphSet<'g> for FeatureSet<'g> {
764 type Id = FeatureId<'g>;
765 type Metadata = FeatureMetadata<'g>;
766
767 fn len(&self) -> usize {
768 self.len()
769 }
770
771 fn contains(&self, id: Self::Id) -> bool {
772 self.contains(id).unwrap()
773 }
774
775 fn union(&self, other: &Self) -> Self {
776 self.union(other)
777 }
778
779 fn intersection(&self, other: &Self) -> Self {
780 self.intersection(other)
781 }
782
783 fn difference(&self, other: &Self) -> Self {
784 self.difference(other)
785 }
786
787 fn symmetric_difference(&self, other: &Self) -> Self {
788 self.symmetric_difference(other)
789 }
790
791 fn ids(&self, direction: DependencyDirection) -> Vec<Self::Id> {
792 self.feature_ids(direction).collect()
793 }
794
795 fn metadatas(&self, direction: DependencyDirection) -> Vec<Self::Metadata> {
796 self.features(direction).collect()
797 }
798
799 fn root_ids(&self, direction: DependencyDirection) -> Vec<Self::Id> {
800 Self::root_ids(self, direction).collect()
801 }
802
803 fn root_metadatas(&self, direction: DependencyDirection) -> Vec<Self::Metadata> {
804 Self::root_features(self, direction).collect()
805 }
806}
807
808pub fn assert_link_order<'g>(
816 links: impl IntoIterator<Item = PackageLink<'g>>,
817 initial: impl IntoIterator<Item = &'g PackageId>,
818 desc: impl Into<DirectionDesc<'g>>,
819 msg: &str,
820) {
821 let desc = desc.into();
822
823 let mut variable_seen: HashSet<_> = initial.into_iter().collect();
825
826 for link in links {
827 let known_id = desc.known_metadata(&link).id();
828 let variable_id = desc.variable_metadata(&link).id();
829
830 variable_seen.insert(variable_id);
831 assert!(
832 variable_seen.contains(&known_id),
833 "{}: for package '{}': unexpected link {} package seen before any links {} package",
834 msg,
835 &known_id,
836 desc.known_desc,
837 desc.variable_desc,
838 );
839 }
840}
841
842fn dep_link_ptrs<'g>(dep_links: impl IntoIterator<Item = PackageLink<'g>>) -> Vec<PackageLinkPtrs> {
843 let mut triples: Vec<_> = dep_links
844 .into_iter()
845 .map(|link| link.as_inner_ptrs())
846 .collect();
847 triples.sort();
848 triples
849}