1#[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 pub all_features: bool,
12 #[cfg_attr(feature = "clap", arg(long))]
13 pub no_default_features: bool,
15 #[cfg_attr(feature = "clap", arg(short = 'F', long, value_delimiter = ' '))]
16 pub features: Vec<String>,
18}
19
20#[cfg(feature = "cargo_metadata")]
21impl Features {
22 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 }
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 }
170}