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}