1use once_cell::sync::Lazy;
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum OptionValueType {
12 Flag,
14 Required(String), Optional(String), Multiple(String), }
21
22#[derive(Debug, Clone)]
24pub struct OptionInfo {
25 pub long: &'static str,
27 pub short: Option<&'static str>,
29 pub value_type: OptionValueType,
31 pub description: &'static str,
33 pub category: &'static str,
35}
36
37pub static CARGO_OPTIONS_CATALOG: Lazy<HashMap<&'static str, Vec<OptionInfo>>> = Lazy::new(|| {
39 let mut catalog = HashMap::new();
40
41 let common_options = vec![
43 OptionInfo {
44 long: "--message-format",
45 short: None,
46 value_type: OptionValueType::Required("FMT".to_string()),
47 description: "Error format",
48 category: "Options",
49 },
50 OptionInfo {
51 long: "--verbose",
52 short: Some("-v"),
53 value_type: OptionValueType::Flag,
54 description: "Use verbose output (-vv very verbose/build.rs output)",
55 category: "Options",
56 },
57 OptionInfo {
58 long: "--quiet",
59 short: Some("-q"),
60 value_type: OptionValueType::Flag,
61 description: "Do not print cargo log messages",
62 category: "Options",
63 },
64 OptionInfo {
65 long: "--color",
66 short: None,
67 value_type: OptionValueType::Required("WHEN".to_string()),
68 description: "Coloring: auto, always, never",
69 category: "Options",
70 },
71 OptionInfo {
72 long: "--config",
73 short: None,
74 value_type: OptionValueType::Required("KEY=VALUE|PATH".to_string()),
75 description: "Override a configuration value",
76 category: "Options",
77 },
78 OptionInfo {
79 long: "-Z",
80 short: None,
81 value_type: OptionValueType::Required("FLAG".to_string()),
82 description: "Unstable (nightly-only) flags",
83 category: "Options",
84 },
85 ];
86
87 let package_options = vec![
89 OptionInfo {
90 long: "--package",
91 short: Some("-p"),
92 value_type: OptionValueType::Optional("SPEC".to_string()),
93 description: "Package to operate on",
94 category: "Package Selection",
95 },
96 OptionInfo {
97 long: "--workspace",
98 short: None,
99 value_type: OptionValueType::Flag,
100 description: "Operate on all packages in the workspace",
101 category: "Package Selection",
102 },
103 OptionInfo {
104 long: "--exclude",
105 short: None,
106 value_type: OptionValueType::Required("SPEC".to_string()),
107 description: "Exclude packages from the operation",
108 category: "Package Selection",
109 },
110 OptionInfo {
111 long: "--all",
112 short: None,
113 value_type: OptionValueType::Flag,
114 description: "Alias for --workspace (deprecated)",
115 category: "Package Selection",
116 },
117 ];
118
119 let feature_options = vec![
121 OptionInfo {
122 long: "--features",
123 short: Some("-F"),
124 value_type: OptionValueType::Required("FEATURES".to_string()),
125 description: "Space or comma separated list of features to activate",
126 category: "Feature Selection",
127 },
128 OptionInfo {
129 long: "--all-features",
130 short: None,
131 value_type: OptionValueType::Flag,
132 description: "Activate all available features",
133 category: "Feature Selection",
134 },
135 OptionInfo {
136 long: "--no-default-features",
137 short: None,
138 value_type: OptionValueType::Flag,
139 description: "Do not activate the default feature",
140 category: "Feature Selection",
141 },
142 ];
143
144 let compilation_options = vec![
146 OptionInfo {
147 long: "--jobs",
148 short: Some("-j"),
149 value_type: OptionValueType::Required("N".to_string()),
150 description: "Number of parallel jobs",
151 category: "Compilation Options",
152 },
153 OptionInfo {
154 long: "--release",
155 short: Some("-r"),
156 value_type: OptionValueType::Flag,
157 description: "Build artifacts in release mode",
158 category: "Compilation Options",
159 },
160 OptionInfo {
161 long: "--profile",
162 short: None,
163 value_type: OptionValueType::Required("PROFILE-NAME".to_string()),
164 description: "Build artifacts with the specified profile",
165 category: "Compilation Options",
166 },
167 OptionInfo {
168 long: "--target",
169 short: None,
170 value_type: OptionValueType::Optional("TRIPLE".to_string()),
171 description: "Build for the target triple",
172 category: "Compilation Options",
173 },
174 OptionInfo {
175 long: "--target-dir",
176 short: None,
177 value_type: OptionValueType::Required("DIRECTORY".to_string()),
178 description: "Directory for all generated artifacts",
179 category: "Compilation Options",
180 },
181 ];
182
183 let manifest_options = vec![
185 OptionInfo {
186 long: "--manifest-path",
187 short: None,
188 value_type: OptionValueType::Required("PATH".to_string()),
189 description: "Path to Cargo.toml",
190 category: "Manifest Options",
191 },
192 OptionInfo {
193 long: "--locked",
194 short: None,
195 value_type: OptionValueType::Flag,
196 description: "Assert that Cargo.lock will remain unchanged",
197 category: "Manifest Options",
198 },
199 OptionInfo {
200 long: "--offline",
201 short: None,
202 value_type: OptionValueType::Flag,
203 description: "Run without accessing the network",
204 category: "Manifest Options",
205 },
206 OptionInfo {
207 long: "--frozen",
208 short: None,
209 value_type: OptionValueType::Flag,
210 description: "Equivalent to specifying both --locked and --offline",
211 category: "Manifest Options",
212 },
213 ];
214
215 let mut run_options = vec![];
217 run_options.extend(common_options.clone());
218 run_options.extend(package_options.clone());
219 run_options.extend(feature_options.clone());
220 run_options.extend(compilation_options.clone());
221 run_options.extend(manifest_options.clone());
222 run_options.extend(vec![
223 OptionInfo {
224 long: "--bin",
225 short: None,
226 value_type: OptionValueType::Optional("NAME".to_string()),
227 description: "Name of the bin target to run",
228 category: "Target Selection",
229 },
230 OptionInfo {
231 long: "--example",
232 short: None,
233 value_type: OptionValueType::Optional("NAME".to_string()),
234 description: "Name of the example target to run",
235 category: "Target Selection",
236 },
237 ]);
238 catalog.insert("run", run_options);
239
240 let mut test_options = vec![];
242 test_options.extend(common_options.clone());
243 test_options.extend(package_options.clone());
244 test_options.extend(feature_options.clone());
245 test_options.extend(compilation_options.clone());
246 test_options.extend(manifest_options.clone());
247 test_options.extend(vec![
248 OptionInfo {
249 long: "--no-run",
250 short: None,
251 value_type: OptionValueType::Flag,
252 description: "Compile, but don't run tests",
253 category: "Options",
254 },
255 OptionInfo {
256 long: "--no-fail-fast",
257 short: None,
258 value_type: OptionValueType::Flag,
259 description: "Run all tests regardless of failure",
260 category: "Options",
261 },
262 OptionInfo {
263 long: "--lib",
264 short: None,
265 value_type: OptionValueType::Flag,
266 description: "Test only this package's library",
267 category: "Target Selection",
268 },
269 OptionInfo {
270 long: "--bins",
271 short: None,
272 value_type: OptionValueType::Flag,
273 description: "Test all binaries",
274 category: "Target Selection",
275 },
276 OptionInfo {
277 long: "--bin",
278 short: None,
279 value_type: OptionValueType::Optional("NAME".to_string()),
280 description: "Test only the specified binary",
281 category: "Target Selection",
282 },
283 OptionInfo {
284 long: "--examples",
285 short: None,
286 value_type: OptionValueType::Flag,
287 description: "Test all examples",
288 category: "Target Selection",
289 },
290 OptionInfo {
291 long: "--example",
292 short: None,
293 value_type: OptionValueType::Optional("NAME".to_string()),
294 description: "Test only the specified example",
295 category: "Target Selection",
296 },
297 OptionInfo {
298 long: "--tests",
299 short: None,
300 value_type: OptionValueType::Flag,
301 description: "Test all test targets",
302 category: "Target Selection",
303 },
304 OptionInfo {
305 long: "--test",
306 short: None,
307 value_type: OptionValueType::Optional("NAME".to_string()),
308 description: "Test only the specified test target",
309 category: "Target Selection",
310 },
311 OptionInfo {
312 long: "--benches",
313 short: None,
314 value_type: OptionValueType::Flag,
315 description: "Test all bench targets",
316 category: "Target Selection",
317 },
318 OptionInfo {
319 long: "--bench",
320 short: None,
321 value_type: OptionValueType::Optional("NAME".to_string()),
322 description: "Test only the specified bench target",
323 category: "Target Selection",
324 },
325 OptionInfo {
326 long: "--all-targets",
327 short: None,
328 value_type: OptionValueType::Flag,
329 description: "Test all targets (does not include doctests)",
330 category: "Target Selection",
331 },
332 OptionInfo {
333 long: "--doc",
334 short: None,
335 value_type: OptionValueType::Flag,
336 description: "Test only this library's documentation",
337 category: "Target Selection",
338 },
339 ]);
340 catalog.insert("test", test_options);
341
342 let mut build_options = vec![];
344 build_options.extend(common_options.clone());
345 build_options.extend(package_options.clone());
346 build_options.extend(feature_options.clone());
347 build_options.extend(compilation_options.clone());
348 build_options.extend(manifest_options.clone());
349 build_options.extend(vec![
350 OptionInfo {
351 long: "--lib",
352 short: None,
353 value_type: OptionValueType::Flag,
354 description: "Build only this package's library",
355 category: "Target Selection",
356 },
357 OptionInfo {
358 long: "--bins",
359 short: None,
360 value_type: OptionValueType::Flag,
361 description: "Build all binaries",
362 category: "Target Selection",
363 },
364 OptionInfo {
365 long: "--bin",
366 short: None,
367 value_type: OptionValueType::Optional("NAME".to_string()),
368 description: "Build only the specified binary",
369 category: "Target Selection",
370 },
371 OptionInfo {
372 long: "--examples",
373 short: None,
374 value_type: OptionValueType::Flag,
375 description: "Build all examples",
376 category: "Target Selection",
377 },
378 OptionInfo {
379 long: "--example",
380 short: None,
381 value_type: OptionValueType::Optional("NAME".to_string()),
382 description: "Build only the specified example",
383 category: "Target Selection",
384 },
385 OptionInfo {
386 long: "--tests",
387 short: None,
388 value_type: OptionValueType::Flag,
389 description: "Build all test targets",
390 category: "Target Selection",
391 },
392 OptionInfo {
393 long: "--test",
394 short: None,
395 value_type: OptionValueType::Optional("NAME".to_string()),
396 description: "Build only the specified test target",
397 category: "Target Selection",
398 },
399 OptionInfo {
400 long: "--benches",
401 short: None,
402 value_type: OptionValueType::Flag,
403 description: "Build all bench targets",
404 category: "Target Selection",
405 },
406 OptionInfo {
407 long: "--bench",
408 short: None,
409 value_type: OptionValueType::Optional("NAME".to_string()),
410 description: "Build only the specified bench target",
411 category: "Target Selection",
412 },
413 OptionInfo {
414 long: "--all-targets",
415 short: None,
416 value_type: OptionValueType::Flag,
417 description: "Build all targets",
418 category: "Target Selection",
419 },
420 OptionInfo {
421 long: "--keep-going",
422 short: None,
423 value_type: OptionValueType::Flag,
424 description: "Do not abort the build as soon as there is an error",
425 category: "Compilation Options",
426 },
427 ]);
428 catalog.insert("build", build_options);
429
430 let mut bench_options = vec![];
432 bench_options.extend(common_options.clone());
433 bench_options.extend(package_options.clone());
434 bench_options.extend(feature_options.clone());
435 bench_options.extend(compilation_options.clone());
436 bench_options.extend(manifest_options.clone());
437 bench_options.extend(vec![
438 OptionInfo {
439 long: "--no-run",
440 short: None,
441 value_type: OptionValueType::Flag,
442 description: "Compile, but don't run benchmarks",
443 category: "Options",
444 },
445 OptionInfo {
446 long: "--no-fail-fast",
447 short: None,
448 value_type: OptionValueType::Flag,
449 description: "Run all benchmarks regardless of failure",
450 category: "Options",
451 },
452 OptionInfo {
454 long: "--lib",
455 short: None,
456 value_type: OptionValueType::Flag,
457 description: "Benchmark only this package's library",
458 category: "Target Selection",
459 },
460 OptionInfo {
461 long: "--bins",
462 short: None,
463 value_type: OptionValueType::Flag,
464 description: "Benchmark all binaries",
465 category: "Target Selection",
466 },
467 OptionInfo {
468 long: "--bin",
469 short: None,
470 value_type: OptionValueType::Optional("NAME".to_string()),
471 description: "Benchmark only the specified binary",
472 category: "Target Selection",
473 },
474 OptionInfo {
475 long: "--examples",
476 short: None,
477 value_type: OptionValueType::Flag,
478 description: "Benchmark all examples",
479 category: "Target Selection",
480 },
481 OptionInfo {
482 long: "--example",
483 short: None,
484 value_type: OptionValueType::Optional("NAME".to_string()),
485 description: "Benchmark only the specified example",
486 category: "Target Selection",
487 },
488 OptionInfo {
489 long: "--tests",
490 short: None,
491 value_type: OptionValueType::Flag,
492 description: "Benchmark all test targets",
493 category: "Target Selection",
494 },
495 OptionInfo {
496 long: "--test",
497 short: None,
498 value_type: OptionValueType::Optional("NAME".to_string()),
499 description: "Benchmark only the specified test target",
500 category: "Target Selection",
501 },
502 OptionInfo {
503 long: "--benches",
504 short: None,
505 value_type: OptionValueType::Flag,
506 description: "Benchmark all bench targets",
507 category: "Target Selection",
508 },
509 OptionInfo {
510 long: "--bench",
511 short: None,
512 value_type: OptionValueType::Optional("NAME".to_string()),
513 description: "Benchmark only the specified bench target",
514 category: "Target Selection",
515 },
516 OptionInfo {
517 long: "--all-targets",
518 short: None,
519 value_type: OptionValueType::Flag,
520 description: "Benchmark all targets",
521 category: "Target Selection",
522 },
523 ]);
524 catalog.insert("bench", bench_options);
525
526 let mut check_options = vec![];
528 check_options.extend(common_options.clone());
529 check_options.extend(package_options.clone());
530 check_options.extend(feature_options.clone());
531 check_options.extend(compilation_options.clone());
532 check_options.extend(manifest_options);
533 check_options.extend(vec![
534 OptionInfo {
535 long: "--lib",
536 short: None,
537 value_type: OptionValueType::Flag,
538 description: "Check only this package's library",
539 category: "Target Selection",
540 },
541 OptionInfo {
542 long: "--bins",
543 short: None,
544 value_type: OptionValueType::Flag,
545 description: "Check all binaries",
546 category: "Target Selection",
547 },
548 OptionInfo {
549 long: "--bin",
550 short: None,
551 value_type: OptionValueType::Optional("NAME".to_string()),
552 description: "Check only the specified binary",
553 category: "Target Selection",
554 },
555 OptionInfo {
556 long: "--examples",
557 short: None,
558 value_type: OptionValueType::Flag,
559 description: "Check all examples",
560 category: "Target Selection",
561 },
562 OptionInfo {
563 long: "--example",
564 short: None,
565 value_type: OptionValueType::Optional("NAME".to_string()),
566 description: "Check only the specified example",
567 category: "Target Selection",
568 },
569 OptionInfo {
570 long: "--tests",
571 short: None,
572 value_type: OptionValueType::Flag,
573 description: "Check all test targets",
574 category: "Target Selection",
575 },
576 OptionInfo {
577 long: "--test",
578 short: None,
579 value_type: OptionValueType::Optional("NAME".to_string()),
580 description: "Check only the specified test target",
581 category: "Target Selection",
582 },
583 OptionInfo {
584 long: "--benches",
585 short: None,
586 value_type: OptionValueType::Flag,
587 description: "Check all bench targets",
588 category: "Target Selection",
589 },
590 OptionInfo {
591 long: "--bench",
592 short: None,
593 value_type: OptionValueType::Optional("NAME".to_string()),
594 description: "Check only the specified bench target",
595 category: "Target Selection",
596 },
597 OptionInfo {
598 long: "--all-targets",
599 short: None,
600 value_type: OptionValueType::Flag,
601 description: "Check all targets",
602 category: "Target Selection",
603 },
604 OptionInfo {
605 long: "--keep-going",
606 short: None,
607 value_type: OptionValueType::Flag,
608 description: "Do not abort the build as soon as there is an error",
609 category: "Compilation Options",
610 },
611 ]);
612 catalog.insert("check", check_options);
613
614 catalog
615});
616
617pub fn is_cargo_option(subcommand: &str, option: &str) -> bool {
619 if let Some(options) = CARGO_OPTIONS_CATALOG.get(subcommand) {
620 options
621 .iter()
622 .any(|opt| opt.long == option || (opt.short == Some(option)))
623 } else {
624 false
625 }
626}
627
628pub fn get_option_info(subcommand: &str, option: &str) -> Option<&'static OptionInfo> {
630 CARGO_OPTIONS_CATALOG
631 .get(subcommand)?
632 .iter()
633 .find(|opt| opt.long == option || (opt.short == Some(option)))
634}
635
636pub fn parse_option_value(
638 option: &str,
639 tokens: &[String],
640 index: usize,
641) -> (Option<String>, usize) {
642 if let Some(info) = CARGO_OPTIONS_CATALOG
643 .values()
644 .flatten()
645 .find(|opt| opt.long == option || (opt.short == Some(option)))
646 {
647 match &info.value_type {
648 OptionValueType::Flag => (None, 0),
649 OptionValueType::Required(_) => {
650 if index + 1 < tokens.len() && !tokens[index + 1].starts_with('-') {
651 (Some(tokens[index + 1].clone()), 1)
652 } else {
653 (None, 0)
655 }
656 }
657 OptionValueType::Optional(_) => {
658 if index + 1 < tokens.len() && !tokens[index + 1].starts_with('-') {
659 (Some(tokens[index + 1].clone()), 1)
660 } else {
661 (None, 0)
662 }
663 }
664 OptionValueType::Multiple(_) => {
665 let mut values = vec![];
667 let mut consumed = 0;
668 for token in tokens.iter().skip(index + 1) {
669 if token.starts_with('-') {
670 break;
671 }
672 values.push(token.clone());
673 consumed += 1;
674 }
675 if values.is_empty() {
676 (None, 0)
677 } else {
678 (Some(values.join(" ")), consumed)
679 }
680 }
681 }
682 } else {
683 (None, 0)
684 }
685}