clap_cargo/
features.rs

1//! Cargo Feature Flags.
2
3/// Cargo Feature Flags.
4#[derive(Default, Clone, Debug, PartialEq, Eq)]
5#[cfg_attr(feature = "clap", derive(clap::Args))]
6#[cfg_attr(feature = "clap", command(about = None, long_about = None))]
7#[non_exhaustive]
8pub struct Features {
9    #[cfg_attr(feature = "clap", arg(long))]
10    /// Activate all available features
11    pub all_features: bool,
12    #[cfg_attr(feature = "clap", arg(long))]
13    /// Do not activate the `default` feature
14    pub no_default_features: bool,
15    #[cfg_attr(feature = "clap", arg(short = 'F', long, value_delimiter = ' '))]
16    /// Space-separated list of features to activate
17    pub features: Vec<String>,
18}
19
20#[cfg(feature = "cargo_metadata")]
21impl Features {
22    /// Forward these flags to the `cargo_metadata` crate.
23    ///
24    /// Note: Requires the features `cargo_metadata`.
25    pub fn forward_metadata<'m>(
26        &self,
27        meta: &'m mut cargo_metadata::MetadataCommand,
28    ) -> &'m mut cargo_metadata::MetadataCommand {
29        if self.all_features {
30            meta.features(cargo_metadata::CargoOpt::AllFeatures);
31        }
32        if self.no_default_features {
33            meta.features(cargo_metadata::CargoOpt::NoDefaultFeatures);
34        }
35        if !self.features.is_empty() {
36            meta.features(cargo_metadata::CargoOpt::SomeFeatures(
37                self.features.clone(),
38            ));
39        }
40        meta
41    }
42}
43
44#[cfg(test)]
45mod test {
46    use super::*;
47
48    #[test]
49    #[cfg(feature = "clap")]
50    fn verify_app() {
51        #[derive(Debug, clap::Parser)]
52        struct Cli {
53            #[command(flatten)]
54            features: Features,
55        }
56
57        use clap::CommandFactory;
58        Cli::command().debug_assert();
59    }
60
61    #[test]
62    #[cfg(feature = "clap")]
63    fn parse_multiple_occurrences() {
64        use clap::Parser;
65
66        #[derive(PartialEq, Eq, Debug, Parser)]
67        struct Args {
68            positional: Option<String>,
69            #[command(flatten)]
70            features: Features,
71        }
72
73        assert_eq!(
74            Args {
75                positional: None,
76                features: Features {
77                    all_features: false,
78                    no_default_features: false,
79                    features: vec![]
80                }
81            },
82            Args::parse_from(["test"])
83        );
84        assert_eq!(
85            Args {
86                positional: Some("foo".to_owned()),
87                features: Features {
88                    all_features: false,
89                    no_default_features: false,
90                    features: vec![]
91                }
92            },
93            Args::parse_from(["test", "foo"])
94        );
95        assert_eq!(
96            Args {
97                positional: None,
98                features: Features {
99                    all_features: false,
100                    no_default_features: false,
101                    features: vec!["foo".to_owned()]
102                }
103            },
104            Args::parse_from(["test", "--features", "foo"])
105        );
106        assert_eq!(
107            Args {
108                positional: None,
109                features: Features {
110                    all_features: false,
111                    no_default_features: false,
112                    features: vec!["foo".to_owned(), "bar".to_owned()]
113                }
114            },
115            Args::parse_from(["test", "--features", "foo bar"])
116        );
117        assert_eq!(
118            Args {
119                positional: Some("baz".to_owned()),
120                features: Features {
121                    all_features: false,
122                    no_default_features: false,
123                    features: vec!["foo".to_owned(), "bar".to_owned()]
124                }
125            },
126            Args::parse_from(["test", "--features", "foo bar", "baz"])
127        );
128        assert_eq!(
129            Args {
130                positional: Some("baz".to_owned()),
131                features: Features {
132                    all_features: false,
133                    no_default_features: false,
134                    features: vec!["foo".to_owned(), "bar".to_owned()]
135                }
136            },
137            Args::parse_from(["test", "--features", "foo", "--features", "bar", "baz"])
138        );
139    }
140
141    #[cfg(feature = "cargo_metadata")]
142    #[test]
143    fn features_all() {
144        let mut metadata = cargo_metadata::MetadataCommand::new();
145        metadata.manifest_path("tests/fixtures/simple/Cargo.toml");
146
147        let features = Features {
148            all_features: true,
149            ..Default::default()
150        };
151        features.forward_metadata(&mut metadata);
152        metadata.exec().unwrap();
153        // TODO verify we forwarded correctly.
154    }
155
156    #[cfg(feature = "cargo_metadata")]
157    #[test]
158    fn features_none() {
159        let mut metadata = cargo_metadata::MetadataCommand::new();
160        metadata.manifest_path("tests/fixtures/simple/Cargo.toml");
161
162        let features = Features {
163            no_default_features: true,
164            ..Default::default()
165        };
166        features.forward_metadata(&mut metadata);
167        metadata.exec().unwrap();
168        // TODO verify we forwarded correctly.
169    }
170}