1use clap::Parser;
2
3use crate::CargoResult;
4
5#[derive(Debug, Parser)]
6pub struct CheckFlags {
7 #[arg(short, long, value_name = "SPEC", help_heading = "Package Selection")]
9 package: Vec<String>,
10
11 #[arg(long, help_heading = "Package Selection")]
13 workspace: bool,
14
15 #[arg(long, value_name = "SPEC", help_heading = "Package Selection")]
17 exclude: Vec<String>,
18
19 #[arg(long, help_heading = "Package Selection")]
21 all: bool,
22
23 #[arg(long, help_heading = "Target Selection")]
25 lib: bool,
26
27 #[arg(long, help_heading = "Target Selection")]
29 bins: bool,
30
31 #[arg(long, value_name = "NAME", help_heading = "Target Selection")]
33 bin: Option<String>,
34
35 #[arg(long, help_heading = "Target Selection")]
37 examples: bool,
38
39 #[arg(long, value_name = "NAME", help_heading = "Target Selection")]
41 example: Option<String>,
42
43 #[arg(long, help_heading = "Target Selection")]
45 tests: bool,
46
47 #[arg(long, value_name = "NAME", help_heading = "Target Selection")]
49 test: Option<String>,
50
51 #[arg(long, help_heading = "Target Selection")]
53 benches: bool,
54
55 #[arg(long, value_name = "NAME", help_heading = "Target Selection")]
57 bench: Option<String>,
58
59 #[arg(long, help_heading = "Target Selection")]
61 all_targets: bool,
62
63 #[arg(
65 short = 'F',
66 long,
67 value_name = "FEATURES",
68 help_heading = "Feature Selection"
69 )]
70 features: Vec<String>,
71
72 #[arg(long, help_heading = "Feature Selection")]
74 all_features: bool,
75
76 #[arg(long, help_heading = "Feature Selection")]
78 no_default_features: bool,
79
80 #[arg(short = 'Z', value_name = "FLAG")]
82 unstable_flags: Vec<String>,
83
84 #[arg(long, value_name = "N", help_heading = "Compilation Options")]
86 jobs: Option<usize>,
87
88 #[arg(long, help_heading = "Compilation Options")]
90 release: bool,
91
92 #[arg(
94 long,
95 value_name = "PROFILE-NAME",
96 help_heading = "Compilation Options"
97 )]
98 profile: Option<String>,
99
100 #[arg(long, value_name = "TRIPLE", help_heading = "Compilation Options")]
102 target: Vec<String>,
103
104 #[arg(long, value_name = "DIRECTORY", help_heading = "Compilation Options")]
106 target_dir: Option<String>,
107
108 #[arg(long, value_name = "PATH", help_heading = "Manifest Options")]
110 manifest_path: Option<String>,
111
112 #[arg(long, value_name = "PATH", help_heading = "Manifest Options")]
114 lockfile_path: Option<String>,
115
116 #[arg(long, help_heading = "Manifest Options")]
118 ignore_rust_version: bool,
119
120 #[arg(long, help_heading = "Manifest Options")]
122 locked: bool,
123
124 #[arg(long, help_heading = "Manifest Options")]
126 offline: bool,
127
128 #[arg(long, help_heading = "Manifest Options")]
130 frozen: bool,
131}
132
133#[derive(Debug)]
135pub(crate) enum PackageSelection<'a> {
136 Default,
137 Workspace { exclude: &'a [String] },
138 Packages(&'a [String]),
139}
140
141impl CheckFlags {
142 pub(crate) fn package_selection(&self) -> PackageSelection<'_> {
143 if self.workspace || self.all {
144 PackageSelection::Workspace {
145 exclude: &self.exclude,
146 }
147 } else if self.package.is_empty() {
148 debug_assert!(self.exclude.is_empty());
149 PackageSelection::Default
150 } else {
151 debug_assert!(self.exclude.is_empty());
152 PackageSelection::Packages(&self.package)
153 }
154 }
155
156 pub(crate) fn selects_package_targets(
158 &self,
159 package: &cargo_metadata::Package,
160 ) -> CargoResult<bool> {
161 if self.all_targets || !self.has_target_selection() {
162 return Ok(true);
163 }
164
165 for target in &package.targets {
166 if self.selects_target(target)? {
167 return Ok(true);
168 }
169 }
170
171 Ok(false)
172 }
173
174 fn has_target_selection(&self) -> bool {
175 self.lib
176 || self.bins
177 || self.bin.is_some()
178 || self.examples
179 || self.example.is_some()
180 || self.tests
181 || self.test.is_some()
182 || self.benches
183 || self.bench.is_some()
184 }
185
186 fn selects_target(&self, target: &cargo_metadata::Target) -> CargoResult<bool> {
187 let is_lib = target.kind.iter().any(|kind| {
188 matches!(
189 kind,
190 cargo_metadata::TargetKind::Lib
191 | cargo_metadata::TargetKind::RLib
192 | cargo_metadata::TargetKind::DyLib
193 | cargo_metadata::TargetKind::CDyLib
194 | cargo_metadata::TargetKind::StaticLib
195 | cargo_metadata::TargetKind::ProcMacro
196 )
197 });
198
199 if self.lib && is_lib {
200 return Ok(true);
201 }
202 if self.bins && target.is_bin() {
203 return Ok(true);
204 }
205 if target.is_bin() && matches_target_name(self.bin.as_deref(), &target.name)? {
206 return Ok(true);
207 }
208 if self.examples && target.is_example() {
209 return Ok(true);
210 }
211 if target.is_example() && matches_target_name(self.example.as_deref(), &target.name)? {
212 return Ok(true);
213 }
214 if self.tests && (target.is_test() || target.test) {
215 return Ok(true);
216 }
217 if target.is_test() && matches_target_name(self.test.as_deref(), &target.name)? {
218 return Ok(true);
219 }
220 if self.benches && target.is_bench() {
221 return Ok(true);
223 }
224 if target.is_bench() && matches_target_name(self.bench.as_deref(), &target.name)? {
225 return Ok(true);
226 }
227
228 Ok(false)
229 }
230
231 pub fn to_flags(&self) -> Vec<String> {
232 let mut out = Vec::new();
233
234 for spec in self.package.clone() {
235 out.push("--package".to_owned());
236 out.push(spec);
237 }
238 if self.workspace {
239 out.push("--workspace".to_owned());
240 }
241 for spec in self.exclude.clone() {
242 out.push("--exclude".to_owned());
243 out.push(spec);
244 }
245 if self.all {
246 out.push("--all".to_owned());
247 }
248
249 if self.lib {
250 out.push("--lib".to_owned());
251 }
252
253 if self.bins {
254 out.push("--bins".to_owned());
255 }
256 if let Some(b) = self.bin.clone() {
257 out.push("--bin".to_owned());
258 out.push(b);
259 }
260
261 if self.examples {
262 out.push("--examples".to_owned());
263 }
264 if let Some(b) = self.example.clone() {
265 out.push("--example".to_owned());
266 out.push(b);
267 }
268
269 if self.tests {
270 out.push("--tests".to_owned());
271 }
272 if let Some(b) = self.test.clone() {
273 out.push("--test".to_owned());
274 out.push(b);
275 }
276
277 if self.benches {
278 out.push("--benches".to_owned());
279 }
280 if let Some(b) = self.bench.clone() {
281 out.push("--bench".to_owned());
282 out.push(b);
283 }
284
285 if self.all_targets {
286 out.push("--all-targets".to_owned());
287 }
288
289 for i in self.features.clone() {
290 out.push("--features".to_owned());
291 out.push(i);
292 }
293 if self.all_features {
294 out.push("--all-features".to_owned());
295 }
296 if self.no_default_features {
297 out.push("--no-default-features".to_owned());
298 }
299
300 for i in self.unstable_flags.clone() {
301 out.push("-Z".to_owned());
302 out.push(i);
303 }
304
305 if let Some(b) = self.jobs {
306 out.push("--jobs".to_owned());
307 out.push(b.to_string());
308 }
309 if self.release {
310 out.push("--release".to_owned());
311 }
312 if let Some(b) = self.profile.clone() {
313 out.push("--profile".to_owned());
314 out.push(b);
315 }
316
317 for spec in self.target.clone() {
318 out.push("--target".to_owned());
319 out.push(spec);
320 }
321 if let Some(b) = self.target_dir.clone() {
322 out.push("--target-dir".to_owned());
323 out.push(b);
324 }
325
326 if let Some(b) = self.manifest_path.clone() {
327 out.push("--manifest-path".to_owned());
328 out.push(b);
329 }
330 if let Some(b) = self.lockfile_path.clone() {
331 out.push("--lockfile-path".to_owned());
332 out.push(b);
333 }
334 if self.ignore_rust_version {
335 out.push("--ignore-rust-version".to_owned());
336 }
337 if self.locked {
338 out.push("--locked".to_owned());
339 }
340 if self.offline {
341 out.push("--offline".to_owned());
342 }
343 if self.frozen {
344 out.push("--frozen".to_owned());
345 }
346 out
347 }
348
349 pub(crate) fn to_metadata_flags(&self) -> Vec<String> {
353 let mut out = Vec::new();
354
355 for feature in &self.features {
356 out.push("--features".to_owned());
357 out.push(feature.clone());
358 }
359 if self.all_features {
360 out.push("--all-features".to_owned());
361 }
362 if self.no_default_features {
363 out.push("--no-default-features".to_owned());
364 }
365
366 for flag in &self.unstable_flags {
367 out.push("-Z".to_owned());
368 out.push(flag.clone());
369 }
370
371 if let Some(path) = &self.manifest_path {
372 out.push("--manifest-path".to_owned());
373 out.push(path.clone());
374 }
375 if let Some(path) = &self.lockfile_path {
376 out.push("--lockfile-path".to_owned());
377 out.push(path.clone());
378 }
379 if self.locked {
380 out.push("--locked".to_owned());
381 }
382 if self.offline {
383 out.push("--offline".to_owned());
384 }
385 if self.frozen {
386 out.push("--frozen".to_owned());
387 }
388
389 out
390 }
391}
392
393fn matches_target_name(requested: Option<&str>, actual: &str) -> CargoResult<bool> {
394 requested
395 .map(|pattern| {
396 glob::Pattern::new(pattern)
397 .map(|pattern| pattern.matches(actual))
398 .map_err(Into::into)
399 })
400 .unwrap_or(Ok(false))
401}