guppy/graph/feature/
feature_list.rs

1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! A sorted, deduplicated list of features from a single package.
5
6use crate::{
7    graph::{
8        feature::{FeatureId, FeatureLabel},
9        PackageMetadata,
10    },
11    sorted_set::SortedSet,
12    PackageId,
13};
14use std::{fmt, slice, vec};
15
16/// A sorted, deduplicated list of features from a single package.
17///
18/// This provides a convenient way to query and print out lists of features.
19///
20/// Returned by methods on `FeatureSet`.
21#[derive(Clone, Eq, PartialEq)]
22pub struct FeatureList<'g> {
23    package: PackageMetadata<'g>,
24    labels: SortedSet<FeatureLabel<'g>>,
25}
26
27impl<'g> FeatureList<'g> {
28    /// Creates a new `FeatureList` from a package and an iterator over feature labels.
29    pub fn new(
30        package: PackageMetadata<'g>,
31        labels: impl IntoIterator<Item = FeatureLabel<'g>>,
32    ) -> Self {
33        Self {
34            package,
35            labels: labels.into_iter().collect(),
36        }
37    }
38
39    /// Returns the package corresponding to this feature list.
40    pub fn package(&self) -> &PackageMetadata<'g> {
41        &self.package
42    }
43
44    /// Returns true if this feature list contains this feature label.
45    pub fn contains(&self, label: FeatureLabel<'_>) -> bool {
46        self.labels.contains(&label)
47    }
48
49    /// Returns true if this feature list contains the "base" feature.
50    ///
51    /// The "base" feature represents the package with no features enabled.
52    #[inline]
53    pub fn has_base(&self) -> bool {
54        self.contains(FeatureLabel::Base)
55    }
56
57    /// Returns true if this feature list contains the specified named feature.
58    #[inline]
59    pub fn has_named_feature(&self, feature_name: &str) -> bool {
60        self.contains(FeatureLabel::Named(feature_name))
61    }
62
63    /// Returns true if this feature list contains the specified optional dependency.
64    #[inline]
65    pub fn has_optional_dependency(&self, dep_name: &str) -> bool {
66        self.contains(FeatureLabel::OptionalDependency(dep_name))
67    }
68
69    /// Returns the list of labels as a slice.
70    ///
71    /// This slice is guaranteed to be sorted and unique.
72    pub fn labels(&self) -> &[FeatureLabel<'g>] {
73        self.labels.as_slice()
74    }
75
76    /// Returns an iterator containing all named features.
77    ///
78    /// The iterator is guaranteed to be sorted and unique.
79    pub fn named_features(&self) -> impl Iterator<Item = &'g str> + '_ {
80        // XXX: binary search?
81        self.labels.iter().filter_map(|label| match label {
82            FeatureLabel::Named(feature_name) => Some(*feature_name),
83            _ => None,
84        })
85    }
86
87    /// Returns an iterator containing all optional dependencies.
88    ///
89    /// The iterator is guaranteed to be sorted and unique.
90    pub fn optional_deps(&self) -> impl Iterator<Item = &'g str> + '_ {
91        // XXX: binary search?
92        self.labels.iter().filter_map(|label| match label {
93            FeatureLabel::OptionalDependency(dep_name) => Some(*dep_name),
94            _ => None,
95        })
96    }
97
98    /// Returns a borrowed iterator over feature IDs.
99    pub fn iter<'a>(&'a self) -> Iter<'g, 'a> {
100        self.into_iter()
101    }
102
103    /// Returns a pretty-printer over the list of feature labels.
104    pub fn display_features<'a>(&'a self) -> DisplayFeatures<'g, 'a> {
105        DisplayFeatures(self.labels())
106    }
107
108    /// Returns a vector of feature labels.
109    ///
110    /// The vector is guaranteed to be sorted and unique.
111    pub fn into_labels(self) -> Vec<FeatureLabel<'g>> {
112        self.labels.into_inner().into_vec()
113    }
114}
115
116impl fmt::Debug for FeatureList<'_> {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        f.debug_struct("FeatureList")
119            .field("package", self.package.id())
120            .field("labels", &self.display_features())
121            .finish()
122    }
123}
124
125impl<'g> IntoIterator for FeatureList<'g> {
126    type Item = FeatureId<'g>;
127    type IntoIter = IntoIter<'g>;
128
129    fn into_iter(self) -> Self::IntoIter {
130        IntoIter::new(self)
131    }
132}
133
134impl<'a, 'g> IntoIterator for &'a FeatureList<'g> {
135    type Item = FeatureId<'g>;
136    type IntoIter = Iter<'g, 'a>;
137
138    fn into_iter(self) -> Self::IntoIter {
139        Iter::new(self)
140    }
141}
142
143/// An owned iterator over a `FeatureList`.
144pub struct IntoIter<'g> {
145    package_id: &'g PackageId,
146    iter: vec::IntoIter<FeatureLabel<'g>>,
147}
148
149impl<'g> IntoIter<'g> {
150    /// Creates a new iterator.
151    pub fn new(feature_list: FeatureList<'g>) -> Self {
152        Self {
153            package_id: feature_list.package.id(),
154            iter: feature_list.into_labels().into_iter(),
155        }
156    }
157}
158
159impl<'g> Iterator for IntoIter<'g> {
160    type Item = FeatureId<'g>;
161
162    fn next(&mut self) -> Option<Self::Item> {
163        self.iter
164            .next()
165            .map(|label| FeatureId::new(self.package_id, label))
166    }
167}
168
169/// A borrowed iterator over a `FeatureList`.
170pub struct Iter<'g, 'a> {
171    package_id: &'g PackageId,
172    iter: slice::Iter<'a, FeatureLabel<'g>>,
173}
174
175impl<'g, 'a> Iter<'g, 'a> {
176    /// Creates a new iterator.
177    pub fn new(feature_list: &'a FeatureList<'g>) -> Self {
178        Self {
179            package_id: feature_list.package.id(),
180            iter: feature_list.labels().iter(),
181        }
182    }
183}
184
185impl<'g> Iterator for Iter<'g, '_> {
186    type Item = FeatureId<'g>;
187
188    fn next(&mut self) -> Option<Self::Item> {
189        self.iter
190            .next()
191            .map(|&label| FeatureId::new(self.package_id, label))
192    }
193}
194
195/// A pretty-printer for a list of features.
196///
197/// Returned by `FeatureList::display_filters`.
198#[derive(Clone, Copy)]
199pub struct DisplayFeatures<'g, 'a>(&'a [FeatureLabel<'g>]);
200
201impl fmt::Display for DisplayFeatures<'_, '_> {
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        let len = self.0.len();
204        for (idx, label) in self.0.iter().enumerate() {
205            write!(f, "{}", label)?;
206            if idx < len - 1 {
207                write!(f, ", ")?;
208            }
209        }
210        Ok(())
211    }
212}
213
214impl fmt::Debug for DisplayFeatures<'_, '_> {
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        // Use the Display impl as the debug one because it's easier to read.
217        write!(f, "{}", self)
218    }
219}