cargo_feature_matrix/
features.rs

1use crate::Config;
2use cargo_metadata::Package;
3use derive_more::{AsMut, AsRef, Deref, DerefMut};
4use itertools::Itertools;
5use serde::{Deserialize, Serialize};
6use std::{
7  borrow::Cow,
8  collections::BTreeSet,
9  fmt::{Display, Formatter},
10};
11
12#[derive(
13  Clone, Debug, Default, Deref, DerefMut, AsRef, AsMut, Serialize, Deserialize,
14)]
15#[serde(transparent)]
16pub struct FeatureMatrix<'f>(#[serde(borrow)] BTreeSet<FeatureSet<'f>>);
17
18impl<'f> FeatureMatrix<'f> {
19  pub(crate) fn new(package: &'f Package, config: &'f Config<'f>) -> Self {
20    let mut include = config.include.clone();
21    include.add_transitive_features(package);
22    let include = include;
23
24    extract_seed(package, config)
25      .into_iter()
26      .powerset()
27      .map(FeatureSet::from_iter)
28      .map(|mut set| {
29        set.extend(include.clone());
30        set.add_transitive_features(package);
31        set
32      })
33      .filter(|set| set.is_disjoint(&config.deny))
34      .filter(|set| !config.skip.iter().any(|skip| skip == set))
35      .filter(|set| {
36        !config
37          .conflict
38          .iter()
39          .any(|conflict| set.is_superset(conflict))
40      })
41      .collect()
42  }
43}
44
45/// Reads the package + config and outputs the set of features that should be used to
46/// seed the matrix.
47fn extract_seed<'f>(
48  package: &'f Package,
49  config: &'f Config<'f>,
50) -> FeatureSet<'f> {
51  if !config.seed.is_empty() {
52    config.seed.clone()
53  } else {
54    package
55            .features
56            .keys()
57            .map(|feature| Feature(Cow::Borrowed(feature)))
58            // exclude default feature
59            .filter(|feature| feature.0 != "default")
60            .filter(|feature| !feature.starts_with("dep:"))
61            // exclude deny list because they will all end up denied anyways
62            .filter(|package| !config.deny.iter().contains(package))
63            // exclude the include list because it'll be easier to just add them all at once
64            .filter(|package| !config.include.iter().contains(package))
65            // exclude hidden features by default
66            .filter(|feature| {
67                config.include_hidden || !feature.starts_with("__")
68            })
69            .collect()
70  }
71}
72
73#[derive(
74  Clone,
75  Debug,
76  Default,
77  Ord,
78  PartialOrd,
79  Eq,
80  PartialEq,
81  Hash,
82  Deref,
83  DerefMut,
84  AsRef,
85  AsMut,
86  Serialize,
87  Deserialize,
88)]
89#[serde(transparent)]
90pub struct FeatureSet<'f>(BTreeSet<Feature<'f>>);
91
92impl<'f> FeatureSet<'f> {
93  fn add_transitive_features(&mut self, package: &'f Package) {
94    let raw_features = &package.features;
95    let transitive = self
96      .iter()
97      .filter_map(|feature| {
98        raw_features.get(feature.as_ref()).map(|transitives| {
99          transitives
100            .iter()
101            .filter(|transitive| !transitive.starts_with("dep:"))
102            .map(AsRef::as_ref)
103        })
104      })
105      .flatten()
106      .map(Cow::Borrowed)
107      .map(Feature)
108      .collect_vec();
109    self.extend(transitive);
110  }
111}
112
113#[derive(
114  Clone,
115  Debug,
116  Ord,
117  PartialOrd,
118  Eq,
119  PartialEq,
120  Hash,
121  Deref,
122  DerefMut,
123  AsRef,
124  AsMut,
125  Serialize,
126  Deserialize,
127)]
128#[serde(transparent)]
129#[as_ref(forward)]
130#[as_mut(forward)]
131pub struct Feature<'f>(pub(crate) Cow<'f, str>);
132
133impl<'f> FromIterator<FeatureSet<'f>> for FeatureMatrix<'f> {
134  fn from_iter<T: IntoIterator<Item = FeatureSet<'f>>>(iter: T) -> Self {
135    FeatureMatrix(iter.into_iter().collect())
136  }
137}
138
139impl<'f> IntoIterator for FeatureMatrix<'f> {
140  type Item = FeatureSet<'f>;
141  type IntoIter = <<Self as Deref>::Target as IntoIterator>::IntoIter;
142
143  fn into_iter(self) -> Self::IntoIter {
144    self.0.into_iter()
145  }
146}
147
148impl Display for FeatureSet<'_> {
149  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
150    let mut iter = self.iter();
151    if let Some(feature) = iter.next() {
152      Display::fmt(feature, f)?;
153    }
154    for feature in iter {
155      write!(f, ",{}", feature)?;
156    }
157    Ok(())
158  }
159}
160
161impl<'f> FromIterator<Feature<'f>> for FeatureSet<'f> {
162  fn from_iter<T: IntoIterator<Item = Feature<'f>>>(iter: T) -> Self {
163    FeatureSet(iter.into_iter().collect())
164  }
165}
166
167impl<'f> IntoIterator for FeatureSet<'f> {
168  type Item = Feature<'f>;
169  type IntoIter = <<Self as Deref>::Target as IntoIterator>::IntoIter;
170
171  fn into_iter(self) -> Self::IntoIter {
172    self.0.into_iter()
173  }
174}
175
176impl Display for Feature<'_> {
177  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
178    Display::fmt(&self.0, f)
179  }
180}
181
182impl From<String> for Feature<'static> {
183  fn from(s: String) -> Self {
184    Feature(Cow::Owned(s))
185  }
186}