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