1 | //! Cargo Feature Flags. |
2 | |
3 | #[derive (Default, Clone, Debug, PartialEq, Eq)] |
4 | #[cfg_attr (feature = "clap" , derive(clap::Args))] |
5 | #[non_exhaustive ] |
6 | pub 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" )] |
19 | impl 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)] |
43 | mod 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 | |