cli_xtask/workspace/
package.rs

1use cargo_metadata::{camino::Utf8Path, Package};
2
3/// Extension methods for [`cargo_metadata::Package`].
4pub trait PackageExt {
5    /// Returns the iterator over each feature options for the package.
6    fn each_feature(&'_ self) -> EachFeature<'_>;
7
8    /// Returns the package root directory.
9    fn root_directory(&self) -> &Utf8Path;
10}
11
12impl PackageExt for Package {
13    fn each_feature(&'_ self) -> EachFeature<'_> {
14        let mut features = self.features.keys().collect::<Vec<_>>();
15        features.sort();
16        EachFeature {
17            all_features: true,
18            no_default_features: true,
19            features: features.into_iter(),
20        }
21    }
22
23    fn root_directory(&self) -> &Utf8Path {
24        // `manifest_path` is the path to the manifest file, so parent must exist.
25        self.manifest_path.parent().unwrap()
26    }
27}
28
29/// Iterator over each feature options for the package.
30///
31/// This iterator is created by [`PackageExt::each_feature`].
32#[derive(Debug)]
33pub struct EachFeature<'a> {
34    all_features: bool,
35    features: std::vec::IntoIter<&'a String>,
36    no_default_features: bool,
37}
38
39impl<'a> Iterator for EachFeature<'a> {
40    type Item = FeatureOption<'a>;
41
42    fn next(&mut self) -> Option<Self::Item> {
43        if self.all_features {
44            self.all_features = false;
45            return Some(FeatureOption::AllFeatures);
46        }
47        if self.no_default_features {
48            self.no_default_features = false;
49            return Some(FeatureOption::NoDefaultFeatures);
50        }
51        if let Some(feature) = self.features.next() {
52            return Some(FeatureOption::Features(vec![feature]));
53        }
54        None
55    }
56}
57
58/// Feature option for the package.
59#[derive(Debug, Clone)]
60pub enum FeatureOption<'a> {
61    /// `--all-features`
62    AllFeatures,
63    /// `--no-default-features`
64    NoDefaultFeatures,
65    /// `--features <feature> --no-default-features`
66    Features(Vec<&'a str>),
67}
68
69impl<'a> FeatureOption<'a> {
70    /// Convert the value to corresponding cargo option strings.
71    pub fn to_args(&self) -> Vec<&'a str> {
72        match self {
73            Self::AllFeatures => vec!["--all-features"],
74            Self::NoDefaultFeatures => vec!["--no-default-features"],
75            Self::Features(features) => {
76                let mut args = Vec::with_capacity(features.len() * 2 + 1);
77                for feature in features {
78                    args.extend(["--features", feature]);
79                }
80                args.push("--no-default-features");
81                args
82            }
83        }
84    }
85}