Skip to main content

guppy/graph/feature/
graph_impl.rs

1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::{
5    DependencyKind, Error, PackageId,
6    errors::FeatureGraphWarning,
7    graph::{
8        DependencyDirection, FeatureIndexInPackage, FeatureIx, PackageGraph, PackageIx,
9        PackageLink, PackageMetadata,
10        feature::{
11            Cycles, FeatureFilter, FeatureList, WeakDependencies, WeakIndex,
12            build::{FeatureGraphBuildState, FeaturePetgraph},
13        },
14    },
15    petgraph_support::{scc::Sccs, topo::TopoWithCycles},
16    platform::{PlatformStatus, PlatformStatusImpl},
17};
18use ahash::AHashMap;
19use debug_ignore::DebugIgnore;
20use once_cell::sync::OnceCell;
21use petgraph::{
22    algo::has_path_connecting,
23    prelude::*,
24    visit::{EdgeFiltered, IntoNodeReferences},
25};
26use smallvec::SmallVec;
27use std::{fmt, iter, iter::FromIterator};
28
29// Some general notes about feature graphs:
30//
31// The set of features for a package is the named features (in the [features] section), plus any
32// optional dependencies.
33//
34// An optional dependency can be either normal or build -- not dev. Note that a dependency can be
35// marked optional in one section and required in another. In this context, a dependency is a
36// feature if it is marked as optional in any context.
37//
38// Features are *unified*. See the documentation in add_dependency_edges for more.
39//
40// There are a few ways features can be enabled. The most common is within a dependency spec. A
41// feature can also be specified via the command-line. Finally, named features can specify what
42// features a package depends on:
43//
44// ```toml
45// [features]
46// foo = ["a/bar", "optional-dep", "baz"]
47// baz = []
48// ```
49//
50// Feature names are unique. A named feature and an optional dep cannot have the same names.
51
52impl PackageGraph {
53    /// Returns a derived graph representing every feature of every package.
54    ///
55    /// The feature graph is constructed the first time this method is called. The graph is cached
56    /// so that repeated calls to this method are cheap.
57    pub fn feature_graph(&self) -> FeatureGraph<'_> {
58        let inner = self.get_feature_graph();
59        FeatureGraph {
60            package_graph: self,
61            inner,
62        }
63    }
64
65    pub(super) fn get_feature_graph(&self) -> &FeatureGraphImpl {
66        self.feature_graph
67            .get_or_init(|| FeatureGraphImpl::new(self))
68    }
69}
70
71/// A derived graph representing every feature of every package.
72///
73/// Constructed through `PackageGraph::feature_graph`.
74#[derive(Clone, Copy, Debug)]
75pub struct FeatureGraph<'g> {
76    pub(crate) package_graph: &'g PackageGraph,
77    pub(super) inner: &'g FeatureGraphImpl,
78}
79
80assert_covariant!(FeatureGraph);
81
82impl<'g> FeatureGraph<'g> {
83    /// Returns any non-fatal warnings encountered while constructing the feature graph.
84    pub fn build_warnings(&self) -> &'g [FeatureGraphWarning] {
85        &self.inner.warnings
86    }
87
88    /// Returns the `PackageGraph` from which this feature graph was constructed.
89    pub fn package_graph(&self) -> &'g PackageGraph {
90        self.package_graph
91    }
92
93    /// Returns the total number of (package ID, feature) combinations in this graph.
94    ///
95    /// Includes the "base" feature for each package.
96    pub fn feature_count(&self) -> usize {
97        self.dep_graph().node_count()
98    }
99
100    /// Returns the number of links in this graph.
101    pub fn link_count(&self) -> usize {
102        self.dep_graph().edge_count()
103    }
104
105    /// Returns true if this feature graph contains the specified feature.
106    pub fn contains(&self, feature_id: impl Into<FeatureId<'g>>) -> bool {
107        let feature_id = feature_id.into();
108        FeatureNode::from_id(self, feature_id).is_some()
109    }
110
111    /// Returns metadata for the given feature ID, or `None` if the feature wasn't found.
112    pub fn metadata(
113        &self,
114        feature_id: impl Into<FeatureId<'g>>,
115    ) -> Result<FeatureMetadata<'g>, Error> {
116        let feature_id = feature_id.into();
117        let feature_node = FeatureNode::from_id(self, feature_id)
118            .ok_or_else(|| Error::unknown_feature_id(feature_id))?;
119        self.metadata_for_node(feature_node)
120            .ok_or_else(|| Error::unknown_feature_id(feature_id))
121    }
122
123    /// Returns all known features for a package.
124    ///
125    /// Returns an error if the package ID was unknown.
126    pub fn all_features_for(&self, package_id: &PackageId) -> Result<FeatureList<'g>, Error> {
127        let package = self.package_graph.metadata(package_id)?;
128        let dep_graph = self.dep_graph();
129        let features = self
130            .feature_ixs_for_package_ix(package.package_ix())
131            .map(|feature_ix| FeatureId::node_to_feature(package, &dep_graph[feature_ix]));
132        Ok(FeatureList::new(package, features))
133    }
134
135    /// Returns true if this feature is included in a package's build by default.
136    ///
137    /// Returns an error if this feature ID is unknown.
138    ///
139    /// ## Cycles
140    ///
141    /// A cyclic dev-dependency may cause additional features to be turned on. This computation
142    /// does *not* follow conditional links and will *not* return true for such additional
143    /// features.
144    pub fn is_default_feature<'a>(
145        &self,
146        feature_id: impl Into<FeatureId<'a>>,
147    ) -> Result<bool, Error> {
148        let feature_id = feature_id.into();
149        let default_ix = self.feature_ix(
150            self.package_graph
151                .metadata(feature_id.package_id())?
152                .default_feature_id(),
153        )?;
154        let feature_ix = self.feature_ix(feature_id)?;
155        // Do not follow conditional links.
156        Ok(self.feature_ix_depends_on_no_conditional(default_ix, feature_ix))
157    }
158
159    /// Returns true if `feature_a` depends (directly or indirectly) on `feature_b`.
160    ///
161    /// In other words, this returns true if `feature_b` is a (possibly transitive) dependency of
162    /// `feature_a`.
163    ///
164    /// This also returns true if `feature_a` is the same as `feature_b`.
165    ///
166    /// Note that this returns true if `feature_a` [conditionally depends on][ConditionalLink] `feature_b`.
167    pub fn depends_on<'a>(
168        &self,
169        feature_a: impl Into<FeatureId<'a>>,
170        feature_b: impl Into<FeatureId<'a>>,
171    ) -> Result<bool, Error> {
172        let feature_a = feature_a.into();
173        let feature_b = feature_b.into();
174        let a_ix = self.feature_ix(feature_a)?;
175        let b_ix = self.feature_ix(feature_b)?;
176        Ok(self.feature_ix_depends_on(a_ix, b_ix))
177    }
178
179    /// Returns true if `feature_a` directly depends on `feature_b`.
180    ///
181    /// In other words, this returns true if `feature_b` is a direct dependency of `feature_a`.
182    ///
183    /// If `feature_a` is the same as `feature_b`, this returns true only if
184    /// the feature has a self-loop edge in the feature graph.
185    pub fn directly_depends_on<'a>(
186        &self,
187        feature_a: impl Into<FeatureId<'a>>,
188        feature_b: impl Into<FeatureId<'a>>,
189    ) -> Result<bool, Error> {
190        let feature_a = feature_a.into();
191        let feature_b = feature_b.into();
192        let a_ix = self.feature_ix(feature_a)?;
193        let b_ix = self.feature_ix(feature_b)?;
194        Ok(self.dep_graph().contains_edge(a_ix, b_ix))
195    }
196
197    /// Returns information about dependency cycles.
198    ///
199    /// For more information, see the documentation for `Cycles`.
200    pub fn cycles(&self) -> Cycles<'g> {
201        Cycles::new(*self)
202    }
203
204    // ---
205    // Helper methods
206    // ---
207
208    /// Verify basic properties of the feature graph.
209    #[doc(hidden)]
210    pub fn verify(&self) -> Result<(), Error> {
211        let feature_set = self.resolve_all();
212        for conditional_link in feature_set.conditional_links(DependencyDirection::Forward) {
213            let (from, to) = conditional_link.endpoints();
214            let is_any = conditional_link.normal().is_present()
215                || conditional_link.build().is_present()
216                || conditional_link.dev().is_present();
217
218            if !is_any {
219                return Err(Error::FeatureGraphInternalError(format!(
220                    "{} -> {}: no edge info found",
221                    from.feature_id(),
222                    to.feature_id()
223                )));
224            }
225        }
226
227        Ok(())
228    }
229
230    /// Returns the strongly connected components for this feature graph.
231    pub(super) fn sccs(&self) -> &'g Sccs<FeatureIx> {
232        self.inner.sccs.get_or_init(|| {
233            let edge_filtered =
234                EdgeFiltered::from_fn(self.dep_graph(), |edge| match edge.weight() {
235                    FeatureEdge::DependenciesSection(link)
236                    | FeatureEdge::NamedFeatureDepColon(link)
237                    | FeatureEdge::NamedFeatureWithSlash { link, .. } => !link.dev_only(),
238                    FeatureEdge::NamedFeature | FeatureEdge::FeatureToBase => true,
239                });
240            // Sort the entire graph without dev-only edges -- a correct graph would be cycle-free
241            // but we don't currently do a consistency check for this so handle cycles.
242            // TODO: should we check at construction time? or bubble up a warning somehow?
243            let topo = TopoWithCycles::new(&edge_filtered);
244            Sccs::new(&self.inner.graph, |scc| {
245                topo.sort_nodes(scc);
246            })
247        })
248    }
249
250    fn metadata_impl(&self, feature_id: FeatureId<'g>) -> Option<&'g FeatureMetadataImpl> {
251        let feature_node = FeatureNode::from_id(self, feature_id)?;
252        self.metadata_impl_for_node(&feature_node)
253    }
254
255    pub(in crate::graph) fn metadata_for_ix(
256        &self,
257        feature_ix: NodeIndex<FeatureIx>,
258    ) -> FeatureMetadata<'g> {
259        self.metadata_for_node(self.dep_graph()[feature_ix])
260            .expect("valid feature ix")
261    }
262
263    pub(super) fn metadata_for_node(&self, node: FeatureNode) -> Option<FeatureMetadata<'g>> {
264        let inner = self.metadata_impl_for_node(&node)?;
265        Some(FeatureMetadata {
266            graph: DebugIgnore(*self),
267            node,
268            inner,
269        })
270    }
271
272    #[inline]
273    fn metadata_impl_for_node(&self, node: &FeatureNode) -> Option<&'g FeatureMetadataImpl> {
274        self.inner.map.get(node)
275    }
276
277    pub(super) fn dep_graph(&self) -> &'g FeaturePetgraph {
278        &self.inner.graph
279    }
280
281    /// If this is a conditional edge, returns the link or links it is evaluated
282    /// as during a resolve. Otherwise, return None.
283    pub(super) fn edge_to_links(
284        &self,
285        source_ix: NodeIndex<FeatureIx>,
286        target_ix: NodeIndex<FeatureIx>,
287        edge_ix: EdgeIndex<FeatureIx>,
288        edge: Option<&'g FeatureEdge>,
289    ) -> Option<EdgeLinks<'g>> {
290        let edge = edge.unwrap_or_else(|| &self.dep_graph()[edge_ix]);
291        let make = |inner| ConditionalLink::new(*self, source_ix, target_ix, edge_ix, inner);
292
293        match edge {
294            FeatureEdge::NamedFeature | FeatureEdge::FeatureToBase => None,
295            FeatureEdge::DependenciesSection(link) | FeatureEdge::NamedFeatureDepColon(link) => {
296                // Dependency section and dep:foo style conditional links are always non-weak.
297                Some(EdgeLinks::NonWeak(make(link)))
298            }
299            FeatureEdge::NamedFeatureWithSlash { link, slash } => Some(match slash {
300                SlashForm::Weak(weak) => EdgeLinks::Weak {
301                    required: weak.required.as_ref().map(|half| make(half.get())),
302                    optional: make(weak.optional.get()),
303                    index: weak.index,
304                },
305                SlashForm::Strong => EdgeLinks::NonWeak(make(link)),
306            }),
307        }
308    }
309
310    /// If this is a conditional edge, returns the link covering every
311    /// declaration it was derived from.
312    pub(super) fn edge_to_full_link(
313        &self,
314        source_ix: NodeIndex<FeatureIx>,
315        target_ix: NodeIndex<FeatureIx>,
316        edge_ix: EdgeIndex<FeatureIx>,
317        edge: Option<&'g FeatureEdge>,
318    ) -> Option<ConditionalLink<'g>> {
319        let edge = edge.unwrap_or_else(|| &self.dep_graph()[edge_ix]);
320        let link = match edge {
321            FeatureEdge::NamedFeature | FeatureEdge::FeatureToBase => return None,
322            FeatureEdge::DependenciesSection(link)
323            | FeatureEdge::NamedFeatureDepColon(link)
324            | FeatureEdge::NamedFeatureWithSlash { link, slash: _ } => link,
325        };
326        Some(ConditionalLink::new(
327            *self, source_ix, target_ix, edge_ix, link,
328        ))
329    }
330
331    fn feature_ix_depends_on(
332        &self,
333        a_ix: NodeIndex<FeatureIx>,
334        b_ix: NodeIndex<FeatureIx>,
335    ) -> bool {
336        has_path_connecting(self.dep_graph(), a_ix, b_ix, None)
337    }
338
339    fn feature_ix_depends_on_no_conditional(
340        &self,
341        a_ix: NodeIndex<FeatureIx>,
342        b_ix: NodeIndex<FeatureIx>,
343    ) -> bool {
344        // Filter out conditional edges.
345        let edge_filtered =
346            EdgeFiltered::from_fn(self.dep_graph(), |edge_ref| match edge_ref.weight() {
347                FeatureEdge::FeatureToBase | FeatureEdge::NamedFeature => true,
348                FeatureEdge::DependenciesSection(_)
349                | FeatureEdge::NamedFeatureDepColon(_)
350                | FeatureEdge::NamedFeatureWithSlash { .. } => false,
351            });
352        has_path_connecting(&edge_filtered, a_ix, b_ix, None)
353    }
354
355    pub(super) fn feature_ixs_for_package_ix(
356        &self,
357        package_ix: NodeIndex<PackageIx>,
358    ) -> impl Iterator<Item = NodeIndex<FeatureIx>> + use<> {
359        let package_ix = package_ix.index();
360        let base_ix = self.inner.base_ixs[package_ix].index();
361        // base_ixs has (package count + 1) elements so this access is valid.
362        let next_base_ix = self.inner.base_ixs[package_ix + 1].index();
363
364        (base_ix..next_base_ix).map(NodeIndex::new)
365    }
366
367    pub(super) fn feature_ixs_for_package_ixs<I>(
368        &self,
369        package_ixs: I,
370    ) -> impl Iterator<Item = NodeIndex<FeatureIx>> + 'g + use<'g, I>
371    where
372        I: IntoIterator<Item = NodeIndex<PackageIx>> + 'g,
373    {
374        // Create a copy of FeatureGraph that will be moved into the closure below.
375        let this = *self;
376
377        package_ixs
378            .into_iter()
379            .flat_map(move |package_ix| this.feature_ixs_for_package_ix(package_ix))
380    }
381
382    pub(in crate::graph) fn feature_ixs_for_package_ixs_filtered<B>(
383        &self,
384        package_ixs: impl IntoIterator<Item = NodeIndex<PackageIx>>,
385        filter: impl FeatureFilter<'g>,
386    ) -> B
387    where
388        B: FromIterator<NodeIndex<FeatureIx>>,
389    {
390        let mut filter = filter;
391
392        self.feature_ixs_for_package_ixs(package_ixs)
393            .filter(|feature_ix| {
394                let feature_node = &self.dep_graph()[*feature_ix];
395                filter.accept(self, FeatureId::from_node(self.package_graph, feature_node))
396            })
397            .collect()
398    }
399
400    pub(in crate::graph) fn package_ix_for_feature_ix(
401        &self,
402        feature_ix: NodeIndex<FeatureIx>,
403    ) -> NodeIndex<PackageIx> {
404        let feature_node = &self.dep_graph()[feature_ix];
405        feature_node.package_ix()
406    }
407
408    #[allow(dead_code)]
409    pub(super) fn feature_ixs<'a, B>(
410        &self,
411        feature_ids: impl IntoIterator<Item = FeatureId<'g>>,
412    ) -> Result<B, Error>
413    where
414        B: iter::FromIterator<NodeIndex<FeatureIx>>,
415    {
416        feature_ids
417            .into_iter()
418            .map(|feature_id| self.feature_ix(feature_id))
419            .collect()
420    }
421
422    pub(super) fn feature_ix(
423        &self,
424        feature_id: FeatureId<'g>,
425    ) -> Result<NodeIndex<FeatureIx>, Error> {
426        let metadata = self
427            .metadata_impl(feature_id)
428            .ok_or_else(|| Error::unknown_feature_id(feature_id))?;
429        Ok(metadata.feature_ix)
430    }
431}
432
433/// An identifier for a (package, feature) pair in a feature graph.
434///
435/// Returned by various methods on `FeatureGraph` and `FeatureQuery`.
436///
437/// `From` impls are available for `(&'g PackageId, &'g str)` and `(&'g PackageId, Option<&'g str>)`
438/// tuples.
439#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
440pub struct FeatureId<'g> {
441    package_id: &'g PackageId,
442    label: FeatureLabel<'g>,
443}
444
445assert_covariant!(FeatureId);
446
447impl<'g> FeatureId<'g> {
448    /// Creates a new `FeatureId` with the given [`PackageId`] and [`FeatureLabel`].
449    pub fn new(package_id: &'g PackageId, label: FeatureLabel<'g>) -> Self {
450        Self { package_id, label }
451    }
452
453    /// Creates a new `FeatureId` representing a named feature in the `[features]` section,
454    /// or an implicit named feature created by an optional dependency.
455    pub fn named(package_id: &'g PackageId, feature_name: &'g str) -> Self {
456        Self {
457            package_id,
458            label: FeatureLabel::Named(feature_name),
459        }
460    }
461
462    /// Creates a new `FeatureId` representing an optional dependency.
463    pub fn optional_dependency(package_id: &'g PackageId, dep_name: &'g str) -> Self {
464        Self {
465            package_id,
466            label: FeatureLabel::OptionalDependency(dep_name),
467        }
468    }
469
470    /// Creates a new `FeatureId` representing the base feature for a package.
471    pub fn base(package_id: &'g PackageId) -> Self {
472        Self {
473            package_id,
474            label: FeatureLabel::Base,
475        }
476    }
477
478    pub(super) fn from_node(package_graph: &'g PackageGraph, node: &FeatureNode) -> Self {
479        let package_id = &package_graph.dep_graph[node.package_ix];
480        let metadata = package_graph
481            .metadata(package_id)
482            .expect("package ID should have valid metadata");
483        let feature = Self::node_to_feature(metadata, node);
484        Self {
485            package_id,
486            label: feature,
487        }
488    }
489
490    pub(super) fn node_to_feature(
491        metadata: PackageMetadata<'g>,
492        node: &FeatureNode,
493    ) -> FeatureLabel<'g> {
494        metadata.feature_idx_to_label(node.feature_idx)
495    }
496
497    /// Returns the package ID.
498    pub fn package_id(&self) -> &'g PackageId {
499        self.package_id
500    }
501
502    /// Returns the [`FeatureLabel`] associated with the feature.
503    pub fn label(&self) -> FeatureLabel<'g> {
504        self.label
505    }
506
507    /// Returns true if this is the base feature for the package.
508    #[inline]
509    pub fn is_base(&self) -> bool {
510        self.label.kind().is_base()
511    }
512
513    /// Returns true if this is an optional dependency.
514    #[inline]
515    pub fn is_optional_dependency(self) -> bool {
516        self.label.kind().is_optional_dependency()
517    }
518
519    /// Returns true if this is a named feature.
520    #[inline]
521    pub fn is_named(self) -> bool {
522        self.label.kind().is_named()
523    }
524}
525
526impl<'g> From<(&'g PackageId, FeatureLabel<'g>)> for FeatureId<'g> {
527    fn from((package_id, label): (&'g PackageId, FeatureLabel<'g>)) -> Self {
528        FeatureId { package_id, label }
529    }
530}
531
532/// The `Display` impl prints out:
533///
534/// * `{package-id}/[base]` for base features.
535/// * `{package-id}/feature-name` for named features.
536/// * `{package-id}/dep:dep-name` for optional dependencies.
537///
538/// ## Examples
539///
540/// ```
541/// use guppy::PackageId;
542/// use guppy::graph::feature::FeatureId;
543///
544/// let package_id = PackageId::new("region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)");
545///
546/// assert_eq!(
547///     format!("{}", FeatureId::base(&package_id)),
548///     "region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)/[base]"
549/// );
550///
551/// assert_eq!(
552///     format!("{}", FeatureId::named(&package_id, "foo")),
553///     "region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)/foo"
554/// );
555///
556/// assert_eq!(
557///     format!("{}", FeatureId::optional_dependency(&package_id, "bar")),
558///     "region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)/dep:bar"
559/// );
560/// ```
561impl fmt::Display for FeatureId<'_> {
562    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
563        write!(f, "{}/{}", self.package_id, self.label)
564    }
565}
566
567/// A unique identifier for a feature within a specific package. Forms part of a [`FeatureId`].
568#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
569pub enum FeatureLabel<'g> {
570    /// The "base" feature. Every package has one such feature.
571    Base,
572
573    /// This is a named feature in the `[features]` section, or an implicit feature that corresponds to
574    /// an optional dependency.
575    ///
576    /// For versions of Cargo prior to 1.60, optional dependencies always create implicit features
577    /// by the same name. For versions 1.60 and greater, optional dependencies may create implicit
578    /// features if the dependency doesn't exist with the name "dep" in it.
579    Named(&'g str),
580
581    /// This is an optional dependency.
582    OptionalDependency(&'g str),
583}
584
585impl FeatureLabel<'_> {
586    /// Returns the kind of feature this is.
587    ///
588    /// The kind of a feature is simply the enum variant without any associated data.
589    #[inline]
590    pub fn kind(self) -> FeatureKind {
591        match self {
592            Self::Base => FeatureKind::Base,
593            Self::Named(_) => FeatureKind::Named,
594            Self::OptionalDependency(_) => FeatureKind::OptionalDependency,
595        }
596    }
597}
598
599/// The `Display` impl for `FeatureLabel` prints out:
600///
601/// * `[base]` for base labels.
602/// * `feature-name` for optional dependencies.
603/// * `dep:dep-name` for named features.
604///
605/// ## Examples
606///
607/// ```
608/// use guppy::graph::feature::FeatureLabel;
609///
610/// assert_eq!(format!("{}", FeatureLabel::Base), "[base]");
611/// assert_eq!(format!("{}", FeatureLabel::Named("foo")), "foo");
612/// assert_eq!(format!("{}", FeatureLabel::OptionalDependency("bar")), "dep:bar");
613/// ```
614impl fmt::Display for FeatureLabel<'_> {
615    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
616        match self {
617            Self::Base => write!(f, "[base]"),
618            Self::Named(feature_name) => write!(f, "{feature_name}"),
619            Self::OptionalDependency(dep_name) => write!(f, "dep:{dep_name}"),
620        }
621    }
622}
623
624/// Metadata for a feature within a package.
625#[derive(Clone, Copy)]
626pub struct FeatureMetadata<'g> {
627    graph: DebugIgnore<FeatureGraph<'g>>,
628    node: FeatureNode,
629    inner: &'g FeatureMetadataImpl,
630}
631
632assert_covariant!(FeatureMetadata);
633
634impl<'g> FeatureMetadata<'g> {
635    /// Returns the feature ID corresponding to this metadata.
636    pub fn feature_id(&self) -> FeatureId<'g> {
637        FeatureId::from_node(self.graph.package_graph, &self.node)
638    }
639
640    /// Returns the package ID corresponding to this metadata.
641    pub fn package_id(&self) -> &'g PackageId {
642        &self.graph.package_graph.dep_graph[self.package_ix()]
643    }
644
645    /// Returns the package metadata corresponding to this feature metadata.
646    pub fn package(&self) -> PackageMetadata<'g> {
647        self.graph
648            .package_graph
649            .metadata(self.package_id())
650            .expect("valid package ID")
651    }
652
653    /// Returns the label for this feature.
654    pub fn label(&self) -> FeatureLabel<'g> {
655        self.feature_id().label()
656    }
657
658    // ---
659    // Helper methods
660    // ---
661
662    #[inline]
663    pub(in crate::graph) fn package_ix(&self) -> NodeIndex<PackageIx> {
664        self.node.package_ix
665    }
666
667    #[inline]
668    pub(in crate::graph) fn feature_ix(&self) -> NodeIndex<FeatureIx> {
669        self.inner.feature_ix
670    }
671}
672
673impl fmt::Debug for FeatureMetadata<'_> {
674    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
675        f.debug_struct("FeatureMetadata")
676            .field("id", &self.feature_id())
677            .finish()
678    }
679}
680
681/// A graph representing every possible feature of every package, and the connections between them.
682#[derive(Clone, Debug)]
683pub(in crate::graph) struct FeatureGraphImpl {
684    pub(super) graph: FeaturePetgraph,
685    // base ixs consists of the base (start) feature indexes for each package.
686    pub(super) base_ixs: Vec<NodeIndex<FeatureIx>>,
687    pub(super) map: AHashMap<FeatureNode, FeatureMetadataImpl>,
688    pub(super) warnings: Vec<FeatureGraphWarning>,
689    // The strongly connected components of the feature graph. Computed on demand.
690    pub(super) sccs: OnceCell<Sccs<FeatureIx>>,
691    pub(super) weak: WeakDependencies,
692}
693
694impl FeatureGraphImpl {
695    /// Creates a new `FeatureGraph` from this `PackageGraph`.
696    pub(super) fn new(package_graph: &PackageGraph) -> Self {
697        let mut build_state = FeatureGraphBuildState::new(package_graph);
698
699        // Graph returns its node references in order -- check this in debug builds.
700        let mut prev_ix = None;
701        for (package_ix, package_id) in package_graph.dep_graph.node_references() {
702            if let Some(prev_ix) = prev_ix {
703                debug_assert_eq!(package_ix.index(), prev_ix + 1, "package ixs are in order");
704            }
705            prev_ix = Some(package_ix.index());
706
707            let metadata = package_graph
708                .metadata(package_id)
709                .expect("valid package ID");
710            build_state.add_nodes(metadata);
711        }
712
713        build_state.end_nodes();
714
715        // The choice of bottom-up for this loop and the next is pretty arbitrary.
716        for metadata in package_graph
717            .resolve_all()
718            .packages(DependencyDirection::Reverse)
719        {
720            build_state.add_named_feature_edges(metadata);
721        }
722
723        for link in package_graph
724            .resolve_all()
725            .links(DependencyDirection::Reverse)
726        {
727            build_state.add_dependency_edges(link);
728        }
729
730        build_state.build()
731    }
732}
733
734/// A feature dependency that is conditionally activated.
735///
736/// A `ConditionalLink` is typically a link across packages. For example:
737///
738/// ```toml
739/// [package]
740/// name = "main"
741///
742/// [dependencies]
743/// dep = { ... }
744///
745/// [dev-dependencies]
746/// dev-dep = { ... }
747///
748/// [target.'cfg(unix)'.dependencies]
749/// unix-dep = { ... }
750///
751/// [features]
752/// feat = ["dep/feat", "dev-dep/feat", "unix-dep/feat"]
753/// ```
754///
755/// In this example, there are `ConditionalLink`s from `main/feat` to `dep/feat`, `dev-dep/feat` and
756/// `unix-dep/feat`. Each link is only activated if the conditions for it are met. For example,
757/// the link to `dev-dep/feat` is only followed if Cargo is interested in dev-dependencies of `main`.
758///
759/// If a dependency, for example `unix-dep` above, is optional, an implicit feature is created in
760/// the package `main` with the name `unix-dep`. In this case, the dependency from `main/feat` to
761/// `main/unix-dep` is also a `ConditionalLink` representing the same `cfg(unix)` condition.
762#[derive(Copy, Clone)]
763pub struct ConditionalLink<'g> {
764    graph: DebugIgnore<FeatureGraph<'g>>,
765    from: &'g FeatureMetadataImpl,
766    to: &'g FeatureMetadataImpl,
767    edge_ix: EdgeIndex<FeatureIx>,
768    inner: &'g ConditionalLinkImpl,
769}
770
771assert_covariant!(ConditionalLink);
772
773impl<'g> ConditionalLink<'g> {
774    #[allow(dead_code)]
775    pub(super) fn new(
776        graph: FeatureGraph<'g>,
777        source_ix: NodeIndex<FeatureIx>,
778        target_ix: NodeIndex<FeatureIx>,
779        edge_ix: EdgeIndex<FeatureIx>,
780        inner: &'g ConditionalLinkImpl,
781    ) -> Self {
782        let dep_graph = graph.dep_graph();
783        Self {
784            graph: DebugIgnore(graph),
785            from: graph
786                .metadata_impl_for_node(&dep_graph[source_ix])
787                .expect("valid source ix"),
788            to: graph
789                .metadata_impl_for_node(&dep_graph[target_ix])
790                .expect("valid target ix"),
791            edge_ix,
792            inner,
793        }
794    }
795
796    /// Returns the feature which depends on the `to` feature.
797    pub fn from(&self) -> FeatureMetadata<'g> {
798        FeatureMetadata {
799            graph: DebugIgnore(self.graph.0),
800            node: self.graph.dep_graph()[self.from.feature_ix],
801            inner: self.from,
802        }
803    }
804
805    /// Returns the feature which is depended on by the `from` feature.
806    pub fn to(&self) -> FeatureMetadata<'g> {
807        FeatureMetadata {
808            graph: DebugIgnore(self.graph.0),
809            node: self.graph.dep_graph()[self.to.feature_ix],
810            inner: self.to,
811        }
812    }
813
814    /// Returns the endpoints as a pair of features `(from, to)`.
815    pub fn endpoints(&self) -> (FeatureMetadata<'g>, FeatureMetadata<'g>) {
816        (self.from(), self.to())
817    }
818
819    /// Returns details about this feature dependency from the `[dependencies]` section.
820    pub fn normal(&self) -> PlatformStatus<'g> {
821        PlatformStatus::new(&self.inner.normal)
822    }
823
824    /// Returns details about this feature dependency from the `[build-dependencies]` section.
825    pub fn build(&self) -> PlatformStatus<'g> {
826        PlatformStatus::new(&self.inner.build)
827    }
828
829    /// Returns details about this feature dependency from the `[dev-dependencies]` section.
830    pub fn dev(&self) -> PlatformStatus<'g> {
831        PlatformStatus::new(&self.inner.dev)
832    }
833
834    /// Returns details about this feature dependency from the section specified by the given
835    /// dependency kind.
836    pub fn status_for_kind(&self, kind: DependencyKind) -> PlatformStatus<'g> {
837        match kind {
838            DependencyKind::Normal => self.normal(),
839            DependencyKind::Build => self.build(),
840            DependencyKind::Development => self.dev(),
841        }
842    }
843
844    /// Returns true if this edge is dev-only, i.e. code from this edge will not
845    /// be included in normal builds.
846    ///
847    /// This is scoped to the declarations for this link. For example, a
848    /// dependency might be optional as a normal dependency but required as a
849    /// dev-dependency. In that case, the
850    /// [`Required`](LinkDeclarations::Required) version of the link is dev-only
851    /// while the [`Optional`](LinkDeclarations::Optional) half is not.
852    /// [`Unsplit`](LinkDeclarations::Unsplit) is the union of both, so it
853    /// isn't dev-only either.
854    pub fn dev_only(&self) -> bool {
855        self.inner.dev_only()
856    }
857
858    /// Returns the declarations of the dependency that this link's platform
859    /// statuses were derived from.
860    ///
861    /// A package can declare the same dependency more than once, and some of
862    /// those declarations can be optional while others are required. For
863    /// example:
864    ///
865    /// ```toml
866    /// [dependencies]
867    /// foo = { version = "1" }
868    ///
869    /// [build-dependencies]
870    /// foo = { version = "1", optional = true }
871    ///
872    /// [features]
873    /// weak = ["foo?/std"]
874    /// ```
875    ///
876    /// Cargo applies `foo?/std` to each declaration separately.
877    ///
878    /// * The required normal dependency gets `std` as soon as `weak` is
879    ///   enabled.
880    /// * The optional build dependency gets `std` only if `dep:foo` is
881    ///   also activated.
882    ///
883    /// To model this, [`FeatureQuery::resolve_with`] may call the visitor twice
884    /// for the link from `main/weak` to `foo/std`:
885    ///
886    /// * Once with a [`Required`](LinkDeclarations::Required) link. Here,
887    ///   [`normal`](Self::normal) is always enabled and
888    ///   [`build`](Self::build) is never enabled.
889    /// * Once with an [`Optional`](LinkDeclarations::Optional) link. Here,
890    ///   `normal` is never enabled and `build` is always enabled.
891    ///
892    /// Both links have the same [`from`](Self::from) and [`to`](Self::to); this
893    /// method is the way to tell them apart. The link is followed if the
894    /// visitor accepts either one.
895    ///
896    /// If `foo` has no required declarations for this package, the required
897    /// link is skipped.
898    /// When each link is offered depends on the query's direction; see
899    /// [`FeatureLinkVisitor::visit_link`].
900    ///
901    /// Outside of a resolve, the same edge is a single link.
902    /// [`FeatureSet::conditional_links`] returns it once, with
903    /// [`Unsplit`](LinkDeclarations::Unsplit) and the union of both sets of
904    /// statuses.
905    ///
906    /// For other kinds of links, the return value is fixed:
907    ///
908    /// * A link from a feature with `foo/std` to `foo`'s `std` feature is
909    ///   `Unsplit`, since `foo/std` applies to every declaration of `foo` that
910    ///   resolves to that package.
911    /// * A link from a feature with `foo/std` to `dep:foo`, or to a feature
912    ///   named `foo` in the same package, is `Optional`, since only optional
913    ///   declarations of `foo` activate these.
914    /// * A link from a feature with `dep:foo` to `dep:foo` is `Unsplit`, since
915    ///   `dep:foo` activates every declaration of `foo`.
916    /// * A link from a package's base feature into a dependency is `Required`.
917    /// * A link from `dep:foo` into `foo` is `Optional`.
918    ///
919    /// [`FeatureQuery::resolve_with`]: crate::graph::feature::FeatureQuery::resolve_with
920    /// [`FeatureSet::conditional_links`]: crate::graph::feature::FeatureSet::conditional_links
921    /// [`FeatureLinkVisitor::visit_link`]: crate::graph::feature::FeatureLinkVisitor::visit_link
922    pub fn declarations(&self) -> LinkDeclarations {
923        self.inner.declarations
924    }
925
926    /// Returns the `PackageLink`s this `ConditionalLink` was derived from.
927    ///
928    /// This is usually one link, but in some circumstances a single name can
929    /// resolve to several packages. For example, consider this `Cargo.toml`:
930    ///
931    /// ```toml
932    /// [package]
933    /// name = "main"
934    ///
935    /// [dependencies]
936    /// serde = { package = "serde_core", version = "1", optional = true }
937    ///
938    /// # Never enabled on any platform, but still resolved and locked.
939    /// [target.'cfg(any())'.dependencies]
940    /// serde = { version = "1", optional = true }
941    ///
942    /// [features]
943    /// serde = ["dep:serde", "serde/std"]
944    /// ```
945    ///
946    /// The package graph has two links out of `main`, `main -> serde_core` and
947    /// `main -> serde`, both with the dependency name `serde`. The feature
948    /// `serde` produces three conditional links:
949    ///
950    /// * `main/serde -> main/dep:serde`, from `dep:serde`. This activates the
951    ///   dependency name, so this method returns both `main -> serde_core` and
952    ///   `main -> serde`. The link's platform status is the union of theirs.
953    /// * `main/serde -> serde_core/std`, from `serde/std`. This method returns
954    ///   only `main -> serde_core`.
955    /// * `main/serde -> serde/std`, also from `serde/std`. This method returns
956    ///   only `main -> serde`.
957    ///
958    /// A link that covers only some declarations (see
959    /// [`declarations`](Self::declarations)) omits packages with none of those
960    /// declarations. For example, a link from `foo/std` to `dep:foo` covers
961    /// only optional declarations, so it omits a package that is only ever a
962    /// required dependency named `foo`.
963    ///
964    /// The order in which links are returned is unspecified.
965    pub fn package_links(&self) -> impl ExactSizeIterator<Item = PackageLink<'g>> + 'g {
966        let package_graph = self.graph.package_graph;
967        self.inner
968            .package_edge_ixs
969            .iter()
970            .map(move |edge_ix| package_graph.edge_ix_to_link(edge_ix))
971    }
972
973    // ---
974    // Helper methods
975    // ---
976
977    #[allow(dead_code)]
978    pub(super) fn edge_ix(&self) -> EdgeIndex<FeatureIx> {
979        self.edge_ix
980    }
981
982    pub(super) fn endpoints_in(
983        &self,
984        direction: DependencyDirection,
985    ) -> (FeatureMetadata<'g>, FeatureMetadata<'g>) {
986        match direction {
987            DependencyDirection::Forward => (self.from(), self.to()),
988            DependencyDirection::Reverse => (self.to(), self.from()),
989        }
990    }
991}
992
993impl fmt::Debug for ConditionalLink<'_> {
994    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
995        f.debug_struct("ConditionalLink")
996            .field("from", &self.from())
997            .field("to", &self.to())
998            .field("declarations", &self.declarations())
999            .field("normal", &self.normal())
1000            .field("build", &self.build())
1001            .field("dev", &self.dev())
1002            .finish()
1003    }
1004}
1005
1006// ---
1007
1008/// A combination of a package ID and a feature name, forming a node in a `FeatureGraph`.
1009#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1010pub(in crate::graph) struct FeatureNode {
1011    package_ix: NodeIndex<PackageIx>,
1012    feature_idx: FeatureIndexInPackage,
1013}
1014
1015impl FeatureNode {
1016    /// Returns a new feature node.
1017    pub(in crate::graph) fn new(
1018        package_ix: NodeIndex<PackageIx>,
1019        feature_idx: FeatureIndexInPackage,
1020    ) -> Self {
1021        Self {
1022            package_ix,
1023            feature_idx,
1024        }
1025    }
1026
1027    /// Base feature node.
1028    pub(in crate::graph) fn base(package_ix: NodeIndex<PackageIx>) -> Self {
1029        Self {
1030            package_ix,
1031            feature_idx: FeatureIndexInPackage::Base,
1032        }
1033    }
1034
1035    pub(in crate::graph) fn optional_dep(package_ix: NodeIndex<PackageIx>, dep_idx: usize) -> Self {
1036        Self {
1037            package_ix,
1038            feature_idx: FeatureIndexInPackage::OptionalDependency(dep_idx),
1039        }
1040    }
1041
1042    pub(in crate::graph) fn named_feature(
1043        package_ix: NodeIndex<PackageIx>,
1044        named_idx: usize,
1045    ) -> Self {
1046        Self {
1047            package_ix,
1048            feature_idx: FeatureIndexInPackage::Named(named_idx),
1049        }
1050    }
1051
1052    fn from_id(feature_graph: &FeatureGraph<'_>, id: FeatureId<'_>) -> Option<Self> {
1053        let metadata = feature_graph.package_graph.metadata(id.package_id()).ok()?;
1054        Some(FeatureNode::new(
1055            metadata.package_ix(),
1056            metadata.get_feature_idx(id.label())?,
1057        ))
1058    }
1059
1060    pub(super) fn named_features(package: PackageMetadata<'_>) -> impl Iterator<Item = Self> + '_ {
1061        let package_ix = package.package_ix();
1062        package
1063            .named_features_full()
1064            .map(move |(feature_idx, _, _)| Self {
1065                package_ix,
1066                feature_idx,
1067            })
1068    }
1069
1070    pub(super) fn optional_deps(package: PackageMetadata<'_>) -> impl Iterator<Item = Self> + '_ {
1071        let package_ix = package.package_ix();
1072        package
1073            .optional_deps_full()
1074            .map(move |(feature_idx, _)| Self {
1075                package_ix,
1076                feature_idx,
1077            })
1078    }
1079
1080    pub(in crate::graph) fn package_ix(&self) -> NodeIndex<PackageIx> {
1081        self.package_ix
1082    }
1083
1084    pub(in crate::graph) fn package_id_and_feature_label<'g>(
1085        &self,
1086        graph: &'g PackageGraph,
1087    ) -> (&'g PackageId, FeatureLabel<'g>) {
1088        let package_id = &graph.dep_graph[self.package_ix];
1089        let metadata = graph.metadata(package_id).unwrap();
1090        let feature_label = metadata.feature_idx_to_label(self.feature_idx);
1091        (package_id, feature_label)
1092    }
1093}
1094
1095/// The conditional link or links an edge is evaluated as during a resolve.
1096pub(super) enum EdgeLinks<'g> {
1097    /// A non-weak conditional link, evaluated once.
1098    NonWeak(ConditionalLink<'g>),
1099
1100    /// A weak link, `a = ["foo?/b"]`.
1101    Weak {
1102        /// The link covering required declarations of this dependency.
1103        ///
1104        /// This is `None` if the dependency is never declared as required.
1105        required: Option<ConditionalLink<'g>>,
1106
1107        /// The link covering optional declarations of this dependency.
1108        optional: ConditionalLink<'g>,
1109
1110        /// The index of the buffer that holds `optional` back in a forward
1111        /// query.
1112        index: WeakIndex,
1113    },
1114}
1115
1116/// Information about why a feature depends on another feature.
1117///
1118/// Not part of the stable API -- only exposed for FeatureSet::links().
1119#[derive(Clone, Debug)]
1120#[doc(hidden)]
1121pub enum FeatureEdge {
1122    /// This edge is from a feature to its base package.
1123    FeatureToBase,
1124
1125    /// This is a dependency edge, e.g.:
1126    ///
1127    /// ```toml
1128    /// [dependencies]
1129    /// foo = { version = "1", features = ["a", "b"] }
1130    /// ```
1131    ///
1132    /// (The above is conditional in that it isn't a build dependency. Similarly, it could be
1133    /// a target-specific dependency.)
1134    ///
1135    /// This also includes optional dependencies, for which the "from" node is
1136    /// `FeatureLabel::OptionalDependency` rather than `FeatureLabel::Base`.
1137    ///
1138    /// ```toml
1139    /// [dependencies]
1140    /// foo = { version = "1", features = ["a", "b"], optional = true }
1141    /// ```
1142    DependenciesSection(ConditionalLinkImpl),
1143
1144    /// This edge is from a feature depending on other features within the same package:
1145    ///
1146    /// ```toml
1147    /// [features]
1148    /// a = ["b"]
1149    /// ```
1150    NamedFeature,
1151
1152    /// This edge is from a feature to an optional dependency.
1153    ///
1154    /// ```toml
1155    /// [features]
1156    /// a = ["dep:foo"]
1157    /// ```
1158    NamedFeatureDepColon(ConditionalLinkImpl),
1159
1160    /// This is a named feature line of the form
1161    ///
1162    /// ```toml
1163    /// [features]
1164    /// a = ["foo/b"]
1165    /// # or
1166    /// a = ["foo?/b"]
1167    /// ```
1168    NamedFeatureWithSlash {
1169        link: ConditionalLinkImpl,
1170        slash: SlashForm,
1171    },
1172}
1173
1174/// Which form a [`FeatureEdge::NamedFeatureWithSlash`] takes. Not part of the
1175/// stable API.
1176#[derive(Clone, Debug)]
1177#[doc(hidden)]
1178pub enum SlashForm {
1179    /// The feature applies to every declaration of the dependency as soon as it
1180    /// is enabled. There are three ways to get here:
1181    ///
1182    /// * The feature is written without the `?`, as `a = ["foo/b"]`.
1183    /// * It is written as `foo?/b`, but `foo` has no optional declarations for
1184    ///   this package, so `foo?/b` means the same as `foo/b` here.
1185    /// * `a` has both forms, as in `a = ["foo?/b", "foo/b"]`. Both map to the
1186    ///   same edge, and the non-weak form wins.
1187    Strong,
1188
1189    /// The weak form, `a = ["foo?/b"]`, on a package that `foo` has at least
1190    /// one optional declaration for. For those declarations, the feature only
1191    /// applies once `foo` is activated.
1192    Weak(Box<WeakSlashImpl>),
1193}
1194
1195/// Extra data carried by a weak [`FeatureEdge::NamedFeatureWithSlash`]
1196/// (`foo?/b`). Not part of the stable API.
1197#[derive(Clone, Debug)]
1198#[doc(hidden)]
1199pub struct WeakSlashImpl {
1200    /// The half covering the link's required declarations. Absent if there are
1201    /// no required declarations.
1202    pub(super) required: Option<EnabledLink>,
1203
1204    /// The half covering the link's optional declarations. Always present: an
1205    /// edge with no optional declarations is [`SlashForm::Strong`].
1206    pub(super) optional: EnabledLink,
1207
1208    pub(super) index: WeakIndex,
1209}
1210
1211/// A [`ConditionalLinkImpl`] that is enabled for at least one dependency kind
1212/// on at least one platform. Not part of the stable API.
1213#[derive(Clone, Debug)]
1214#[doc(hidden)]
1215pub struct EnabledLink(ConditionalLinkImpl);
1216
1217impl EnabledLink {
1218    pub(super) fn new(link: ConditionalLinkImpl) -> Option<Self> {
1219        (!link.is_never()).then_some(Self(link))
1220    }
1221
1222    pub(super) fn get(&self) -> &ConditionalLinkImpl {
1223        &self.0
1224    }
1225}
1226
1227/// Not part of the stable API -- only exposed for FeatureSet::links().
1228#[derive(Clone, Debug)]
1229#[doc(hidden)]
1230pub struct ConditionalLinkImpl {
1231    pub(super) package_edge_ixs: PackageEdgeIxs,
1232    pub(super) declarations: LinkDeclarations,
1233    pub(super) normal: PlatformStatusImpl,
1234    pub(super) build: PlatformStatusImpl,
1235    pub(super) dev: PlatformStatusImpl,
1236}
1237
1238impl ConditionalLinkImpl {
1239    #[inline]
1240    fn dev_only(&self) -> bool {
1241        self.normal.is_never() && self.build.is_never()
1242    }
1243
1244    #[inline]
1245    pub(super) fn is_never(&self) -> bool {
1246        self.normal.is_never() && self.build.is_never() && self.dev.is_never()
1247    }
1248
1249    /// Unions two links for the same dependency name.
1250    ///
1251    /// A link that is never enabled contributes nothing, including its package
1252    /// edges, so [`ConditionalLink::package_links`] omits it.
1253    pub(super) fn union(mut self, other: Self) -> Self {
1254        debug_assert_eq!(
1255            self.declarations, other.declarations,
1256            "unioned links cover the same declarations"
1257        );
1258        if other.is_never() {
1259            return self;
1260        }
1261        if self.is_never() {
1262            return other;
1263        }
1264        for edge_ix in other.package_edge_ixs.iter() {
1265            debug_assert!(
1266                !self.package_edge_ixs.0.contains(&edge_ix),
1267                "unioned links have distinct package edges"
1268            );
1269        }
1270        self.package_edge_ixs.0.extend(other.package_edge_ixs.0);
1271        self.normal.extend(&other.normal);
1272        self.build.extend(&other.build);
1273        self.dev.extend(&other.dev);
1274        self
1275    }
1276}
1277
1278/// The declarations of a dependency that a [`ConditionalLink`] was derived
1279/// from.
1280///
1281/// Returned by [`ConditionalLink::declarations`]. For more information, see the
1282/// docs for that method.
1283#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
1284pub enum LinkDeclarations {
1285    /// The link was not split by declaration, so it covers every declaration
1286    /// of the dependency.
1287    Unsplit,
1288
1289    /// Only the declarations without `optional = true`.
1290    Required,
1291
1292    /// Only the declarations with `optional = true`.
1293    Optional,
1294}
1295
1296impl LinkDeclarations {
1297    /// Returns true if these declarations include the ones without
1298    /// `optional = true`: that is, for [`Unsplit`](Self::Unsplit) and
1299    /// [`Required`](Self::Required).
1300    ///
1301    /// Prefer this to simply checking equality against `Required`. A link that
1302    /// was not split by declaration is `Unsplit`, even if every declaration it
1303    /// covers is a required one. For example, with:
1304    ///
1305    /// ```toml
1306    /// [dependencies]
1307    /// foo = { version = "1" }
1308    ///
1309    /// [features]
1310    /// a = ["foo/std"]
1311    /// ```
1312    ///
1313    /// the link from `a` to `foo/std` is `Unsplit`, not `Required`.
1314    pub fn includes_required(self) -> bool {
1315        match self {
1316            Self::Unsplit | Self::Required => true,
1317            Self::Optional => false,
1318        }
1319    }
1320
1321    /// Returns true if these declarations include the ones with
1322    /// `optional = true`: that is, for [`Unsplit`](Self::Unsplit) and
1323    /// [`Optional`](Self::Optional).
1324    ///
1325    /// As with [`includes_required`](Self::includes_required), prefer this to
1326    /// checking equality against `Optional`.
1327    pub fn includes_optional(self) -> bool {
1328        match self {
1329            Self::Unsplit | Self::Optional => true,
1330            Self::Required => false,
1331        }
1332    }
1333}
1334
1335/// The package edges a conditional link was derived from.
1336///
1337/// This is always non-empty by construction.
1338#[derive(Clone, Debug)]
1339pub(super) struct PackageEdgeIxs(SmallVec<[EdgeIndex<PackageIx>; 4]>);
1340
1341impl PackageEdgeIxs {
1342    pub(super) fn single(edge_ix: EdgeIndex<PackageIx>) -> Self {
1343        Self(iter::once(edge_ix).collect())
1344    }
1345
1346    pub(super) fn iter(&self) -> impl ExactSizeIterator<Item = EdgeIndex<PackageIx>> + '_ {
1347        self.0.iter().copied()
1348    }
1349}
1350
1351/// Metadata for a particular feature node.
1352#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1353pub(super) struct FeatureMetadataImpl {
1354    pub(super) feature_ix: NodeIndex<FeatureIx>,
1355}
1356
1357/// The kind of a particular feature within a package.
1358///
1359/// Returned by `FeatureMetadata`.
1360#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1361pub enum FeatureKind {
1362    /// The "base" feature. Every package has one such feature.
1363    Base,
1364
1365    /// This is a named feature in the `[features]` section, or an implicit feature that corresponds to
1366    /// an optional dependency.
1367    ///
1368    /// For versions of Cargo prior to 1.60, optional dependencies always create implicit features
1369    /// by the same name. For versions 1.60 and greater, optional dependencies may create implicit
1370    /// features if the dependency doesn't exist with the name "dep" in it.
1371    Named,
1372
1373    /// This is an optional dependency.
1374    OptionalDependency,
1375}
1376
1377impl FeatureKind {
1378    /// Returns true if this is the base feature.
1379    #[inline]
1380    pub fn is_base(self) -> bool {
1381        matches!(self, Self::Base)
1382    }
1383
1384    /// Returns true if this is a named feature.
1385    #[inline]
1386    pub fn is_named(self) -> bool {
1387        matches!(self, Self::Named)
1388    }
1389
1390    /// Returns true if this is an optional dependency.
1391    #[inline]
1392    pub fn is_optional_dependency(self) -> bool {
1393        matches!(self, Self::OptionalDependency)
1394    }
1395}