fixtures/
dep_helpers.rs

1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use 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/// Some of the messages are different based on whether we're testing forward deps or reverse
31/// ones. For forward deps, we use the terms "known" for 'from' and "variable" for 'to'. For
32/// reverse deps it's the other way round.
33#[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    // Compare (dep_name, resolved_name, id) triples.
94    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        // depends_on should agree with the dependencies returned.
129        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    // Check that the dependency metadata returned is consistent with what we expect.
134    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        // XXX maybe compare version requirements?
141    }
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    // Use a BTreeSet for unique identifiers. This is also used later for set operations.
177    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    // The order requirements are weaker than topological -- for forward queries, a dep should show
199    // up at least once in 'to' before it ever shows up in 'from'.
200    assert_link_order(
201        actual_deps,
202        package_set.root_ids(direction),
203        desc,
204        &format!("{msg}: actual link order"),
205    );
206
207    // Do a query in the opposite direction as well to test link order.
208    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    // Checking for pointer equivalence is enough since they both use the same graph as a base.
214    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        // depends_on should agree with this.
229        graph.assert_depends_on(known_details.id(), dep_id, direction, msg);
230
231        // Transitive deps should be transitively closed.
232        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        // Use difference instead of is_subset/is_superset for better error messages.
244        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        // Use difference instead of is_subset/is_superset for better error messages.
267        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    // A package that comes later cannot depend on one that comes earlier.
290    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    // A package that comes later cannot depend on one that comes earlier.
309    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    // The enabled status can't be unknown on the current platform.
323    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    // all_links should be in the correct order.
343    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(&current_platform).is_known(),
355        "{msg}: enabled status known for current platform"
356    );
357    assert!(
358        req.default_features()
359            .enabled_on(&current_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(&current_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    // TODO: Add support for checks around links once they're defined for feature graphs.
381
382    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            // Note that this skips over idx + 1 entries to avoid earlier_package == later_package.
440            // Doing an exhaustive search would be O(n**2) in the number of packages, so just do a
441            // maximum of 20.
442            // TODO: use proptest to generate random queries on the corpus.
443            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            // This is a dependency cycle -- ignore it in not-depends-on checks.
520            // TODO: make this smarter now that cycles are handled in non-dev order.
521            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
808/// Assert that links are presented in the expected order.
809///
810/// For any given package not in the initial set:
811/// * If direction is Forward, the package should appear in the `to` of a link at least once
812///   before it appears in the `from` of a link.
813/// * If direction is Reverse, the package should appear in the `from` of a link at least once
814///   before it appears in the `to` of a link.
815pub 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    // for forward, 'from' is known and 'to' is variable.
824    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}