1use once_cell::sync::Lazy;
7use std::collections::{HashMap, HashSet};
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum OptionType {
12 Flag,
14 Single,
16 Multiple,
18 Repeatable,
20}
21
22#[derive(Debug, Clone)]
24pub struct OptionMetadata {
25 pub option_type: OptionType,
27 pub description: &'static str,
29 pub conflicts_with: Vec<&'static str>,
31}
32
33pub struct CargoOptions {
35 pub common: HashMap<&'static str, OptionMetadata>,
37 pub run: HashMap<&'static str, OptionMetadata>,
39 pub test: HashMap<&'static str, OptionMetadata>,
41 pub build: HashMap<&'static str, OptionMetadata>,
43 pub check: HashMap<&'static str, OptionMetadata>,
45 pub bench: HashMap<&'static str, OptionMetadata>,
47}
48
49pub static CARGO_OPTIONS: Lazy<CargoOptions> = Lazy::new(|| {
51 let mut options = CargoOptions {
52 common: HashMap::new(),
53 run: HashMap::new(),
54 test: HashMap::new(),
55 build: HashMap::new(),
56 check: HashMap::new(),
57 bench: HashMap::new(),
58 };
59
60 options.common.insert(
62 "--package",
63 OptionMetadata {
64 option_type: OptionType::Single,
65 description: "Package to run",
66 conflicts_with: vec!["--workspace"],
67 },
68 );
69
70 options.common.insert(
71 "-p",
72 OptionMetadata {
73 option_type: OptionType::Single,
74 description: "Alias for --package",
75 conflicts_with: vec!["--workspace"],
76 },
77 );
78
79 options.common.insert(
80 "--workspace",
81 OptionMetadata {
82 option_type: OptionType::Flag,
83 description: "Run for all workspace members",
84 conflicts_with: vec!["--package", "-p"],
85 },
86 );
87
88 options.common.insert(
89 "--exclude",
90 OptionMetadata {
91 option_type: OptionType::Multiple,
92 description: "Exclude packages from the operation",
93 conflicts_with: vec![],
94 },
95 );
96
97 options.common.insert(
98 "--features",
99 OptionMetadata {
100 option_type: OptionType::Multiple,
101 description: "Space or comma separated list of features to activate",
102 conflicts_with: vec!["--all-features"],
103 },
104 );
105
106 options.common.insert(
107 "--all-features",
108 OptionMetadata {
109 option_type: OptionType::Flag,
110 description: "Activate all available features",
111 conflicts_with: vec!["--features", "--no-default-features"],
112 },
113 );
114
115 options.common.insert(
116 "--no-default-features",
117 OptionMetadata {
118 option_type: OptionType::Flag,
119 description: "Do not activate the default feature",
120 conflicts_with: vec![],
121 },
122 );
123
124 options.common.insert(
125 "--release",
126 OptionMetadata {
127 option_type: OptionType::Flag,
128 description: "Build in release mode",
129 conflicts_with: vec!["--debug"],
130 },
131 );
132
133 options.common.insert(
134 "--debug",
135 OptionMetadata {
136 option_type: OptionType::Flag,
137 description: "Build in debug mode (default)",
138 conflicts_with: vec!["--release"],
139 },
140 );
141
142 options.common.insert(
143 "--profile",
144 OptionMetadata {
145 option_type: OptionType::Single,
146 description: "Build with the specified profile",
147 conflicts_with: vec![],
148 },
149 );
150
151 options.common.insert(
152 "--target",
153 OptionMetadata {
154 option_type: OptionType::Single,
155 description: "Build for the target triple",
156 conflicts_with: vec![],
157 },
158 );
159
160 options.common.insert(
161 "--target-dir",
162 OptionMetadata {
163 option_type: OptionType::Single,
164 description: "Directory for all generated artifacts",
165 conflicts_with: vec![],
166 },
167 );
168
169 options.common.insert(
170 "--verbose",
171 OptionMetadata {
172 option_type: OptionType::Flag,
173 description: "Use verbose output",
174 conflicts_with: vec!["--quiet"],
175 },
176 );
177
178 options.common.insert(
179 "-v",
180 OptionMetadata {
181 option_type: OptionType::Flag,
182 description: "Alias for --verbose",
183 conflicts_with: vec!["--quiet", "-q"],
184 },
185 );
186
187 options.common.insert(
188 "--quiet",
189 OptionMetadata {
190 option_type: OptionType::Flag,
191 description: "No output printed to stdout",
192 conflicts_with: vec!["--verbose", "-v"],
193 },
194 );
195
196 options.common.insert(
197 "-q",
198 OptionMetadata {
199 option_type: OptionType::Flag,
200 description: "Alias for --quiet",
201 conflicts_with: vec!["--verbose", "-v"],
202 },
203 );
204
205 options.common.insert(
206 "--color",
207 OptionMetadata {
208 option_type: OptionType::Single,
209 description: "Coloring: auto, always, never",
210 conflicts_with: vec![],
211 },
212 );
213
214 options.common.insert(
215 "--message-format",
216 OptionMetadata {
217 option_type: OptionType::Single,
218 description: "Error format",
219 conflicts_with: vec![],
220 },
221 );
222
223 options.common.insert(
224 "--manifest-path",
225 OptionMetadata {
226 option_type: OptionType::Single,
227 description: "Path to Cargo.toml",
228 conflicts_with: vec![],
229 },
230 );
231
232 options.common.insert(
233 "--frozen",
234 OptionMetadata {
235 option_type: OptionType::Flag,
236 description: "Require Cargo.lock and cache are up to date",
237 conflicts_with: vec!["--locked"],
238 },
239 );
240
241 options.common.insert(
242 "--locked",
243 OptionMetadata {
244 option_type: OptionType::Flag,
245 description: "Require Cargo.lock is up to date",
246 conflicts_with: vec!["--frozen"],
247 },
248 );
249
250 options.common.insert(
251 "--offline",
252 OptionMetadata {
253 option_type: OptionType::Flag,
254 description: "Run without accessing the network",
255 conflicts_with: vec![],
256 },
257 );
258
259 options.common.insert(
260 "-Z",
261 OptionMetadata {
262 option_type: OptionType::Repeatable,
263 description: "Unstable (nightly-only) flags to Cargo",
264 conflicts_with: vec![],
265 },
266 );
267
268 options.run.insert(
270 "--bin",
271 OptionMetadata {
272 option_type: OptionType::Single,
273 description: "Name of the bin target to run",
274 conflicts_with: vec!["--example"],
275 },
276 );
277
278 options.run.insert(
279 "--example",
280 OptionMetadata {
281 option_type: OptionType::Single,
282 description: "Name of the example target to run",
283 conflicts_with: vec!["--bin"],
284 },
285 );
286
287 options.run.insert(
290 "--platform",
291 OptionMetadata {
292 option_type: OptionType::Single,
293 description: "Target platform (web, desktop, mobile)",
294 conflicts_with: vec![],
295 },
296 );
297
298 options.run.insert(
299 "--device",
300 OptionMetadata {
301 option_type: OptionType::Single,
302 description: "Build for device (true) or simulator (false)",
303 conflicts_with: vec![],
304 },
305 );
306
307 options.run.insert(
308 "--hot-reload",
309 OptionMetadata {
310 option_type: OptionType::Flag,
311 description: "Enable hot reloading",
312 conflicts_with: vec![],
313 },
314 );
315
316 options.run.insert(
317 "--open",
318 OptionMetadata {
319 option_type: OptionType::Flag,
320 description: "Open the browser on startup",
321 conflicts_with: vec![],
322 },
323 );
324
325 options.run.insert(
326 "--port",
327 OptionMetadata {
328 option_type: OptionType::Single,
329 description: "Port to serve on",
330 conflicts_with: vec![],
331 },
332 );
333
334 options.run.insert(
335 "--host",
336 OptionMetadata {
337 option_type: OptionType::Single,
338 description: "Host to serve on",
339 conflicts_with: vec![],
340 },
341 );
342
343 options.test.insert(
345 "--lib",
346 OptionMetadata {
347 option_type: OptionType::Flag,
348 description: "Test only this package's library",
349 conflicts_with: vec!["--bin", "--bins"],
350 },
351 );
352
353 options.test.insert(
354 "--bin",
355 OptionMetadata {
356 option_type: OptionType::Single,
357 description: "Test only the specified binary",
358 conflicts_with: vec!["--lib", "--bins"],
359 },
360 );
361
362 options.test.insert(
363 "--bins",
364 OptionMetadata {
365 option_type: OptionType::Flag,
366 description: "Test all binaries",
367 conflicts_with: vec!["--lib", "--bin"],
368 },
369 );
370
371 options.test.insert(
372 "--example",
373 OptionMetadata {
374 option_type: OptionType::Single,
375 description: "Test only the specified example",
376 conflicts_with: vec!["--examples"],
377 },
378 );
379
380 options.test.insert(
381 "--examples",
382 OptionMetadata {
383 option_type: OptionType::Flag,
384 description: "Test all examples",
385 conflicts_with: vec!["--example"],
386 },
387 );
388
389 options.test.insert(
390 "--test",
391 OptionMetadata {
392 option_type: OptionType::Single,
393 description: "Test only the specified test target",
394 conflicts_with: vec!["--tests"],
395 },
396 );
397
398 options.test.insert(
399 "--tests",
400 OptionMetadata {
401 option_type: OptionType::Flag,
402 description: "Test all tests",
403 conflicts_with: vec!["--test"],
404 },
405 );
406
407 options.test.insert(
408 "--bench",
409 OptionMetadata {
410 option_type: OptionType::Single,
411 description: "Test only the specified bench target",
412 conflicts_with: vec!["--benches"],
413 },
414 );
415
416 options.test.insert(
417 "--benches",
418 OptionMetadata {
419 option_type: OptionType::Flag,
420 description: "Test all benches",
421 conflicts_with: vec!["--bench"],
422 },
423 );
424
425 options.test.insert(
426 "--all-targets",
427 OptionMetadata {
428 option_type: OptionType::Flag,
429 description: "Test all targets",
430 conflicts_with: vec![],
431 },
432 );
433
434 options.test.insert(
435 "--doc",
436 OptionMetadata {
437 option_type: OptionType::Flag,
438 description: "Test only this library's documentation",
439 conflicts_with: vec![],
440 },
441 );
442
443 options.test.insert(
444 "--no-run",
445 OptionMetadata {
446 option_type: OptionType::Flag,
447 description: "Compile, but don't run tests",
448 conflicts_with: vec![],
449 },
450 );
451
452 options.test.insert(
453 "--no-fail-fast",
454 OptionMetadata {
455 option_type: OptionType::Flag,
456 description: "Run all tests regardless of failure",
457 conflicts_with: vec![],
458 },
459 );
460
461 options.test.insert(
462 "-j",
463 OptionMetadata {
464 option_type: OptionType::Single,
465 description: "Number of parallel jobs",
466 conflicts_with: vec![],
467 },
468 );
469
470 options.test.insert(
471 "--jobs",
472 OptionMetadata {
473 option_type: OptionType::Single,
474 description: "Number of parallel jobs",
475 conflicts_with: vec![],
476 },
477 );
478
479 options.test.insert(
481 "--platform",
482 OptionMetadata {
483 option_type: OptionType::Single,
484 description: "Target platform (web, desktop, mobile)",
485 conflicts_with: vec![],
486 },
487 );
488
489 options.build.insert(
491 "--lib",
492 OptionMetadata {
493 option_type: OptionType::Flag,
494 description: "Build only this package's library",
495 conflicts_with: vec!["--bin", "--bins"],
496 },
497 );
498
499 options.build.insert(
500 "--bin",
501 OptionMetadata {
502 option_type: OptionType::Single,
503 description: "Build only the specified binary",
504 conflicts_with: vec!["--lib", "--bins"],
505 },
506 );
507
508 options.build.insert(
509 "--bins",
510 OptionMetadata {
511 option_type: OptionType::Flag,
512 description: "Build all binaries",
513 conflicts_with: vec!["--lib", "--bin"],
514 },
515 );
516
517 options.build.insert(
518 "--example",
519 OptionMetadata {
520 option_type: OptionType::Single,
521 description: "Build only the specified example",
522 conflicts_with: vec!["--examples"],
523 },
524 );
525
526 options.build.insert(
527 "--examples",
528 OptionMetadata {
529 option_type: OptionType::Flag,
530 description: "Build all examples",
531 conflicts_with: vec!["--example"],
532 },
533 );
534
535 options.build.insert(
536 "--test",
537 OptionMetadata {
538 option_type: OptionType::Single,
539 description: "Build only the specified test target",
540 conflicts_with: vec!["--tests"],
541 },
542 );
543
544 options.build.insert(
545 "--tests",
546 OptionMetadata {
547 option_type: OptionType::Flag,
548 description: "Build all tests",
549 conflicts_with: vec!["--test"],
550 },
551 );
552
553 options.build.insert(
554 "--bench",
555 OptionMetadata {
556 option_type: OptionType::Single,
557 description: "Build only the specified bench target",
558 conflicts_with: vec!["--benches"],
559 },
560 );
561
562 options.build.insert(
563 "--benches",
564 OptionMetadata {
565 option_type: OptionType::Flag,
566 description: "Build all benches",
567 conflicts_with: vec!["--bench"],
568 },
569 );
570
571 options.build.insert(
572 "--all-targets",
573 OptionMetadata {
574 option_type: OptionType::Flag,
575 description: "Build all targets",
576 conflicts_with: vec![],
577 },
578 );
579
580 options.check.insert(
582 "--lib",
583 OptionMetadata {
584 option_type: OptionType::Flag,
585 description: "Check only this package's library",
586 conflicts_with: vec!["--bin", "--bins"],
587 },
588 );
589
590 options.check.insert(
591 "--bin",
592 OptionMetadata {
593 option_type: OptionType::Single,
594 description: "Check only the specified binary",
595 conflicts_with: vec!["--lib", "--bins"],
596 },
597 );
598
599 options.check.insert(
600 "--bins",
601 OptionMetadata {
602 option_type: OptionType::Flag,
603 description: "Check all binaries",
604 conflicts_with: vec!["--lib", "--bin"],
605 },
606 );
607
608 options.check.insert(
609 "--example",
610 OptionMetadata {
611 option_type: OptionType::Single,
612 description: "Check only the specified example",
613 conflicts_with: vec!["--examples"],
614 },
615 );
616
617 options.check.insert(
618 "--examples",
619 OptionMetadata {
620 option_type: OptionType::Flag,
621 description: "Check all examples",
622 conflicts_with: vec!["--example"],
623 },
624 );
625
626 options.check.insert(
627 "--test",
628 OptionMetadata {
629 option_type: OptionType::Single,
630 description: "Check only the specified test target",
631 conflicts_with: vec!["--tests"],
632 },
633 );
634
635 options.check.insert(
636 "--tests",
637 OptionMetadata {
638 option_type: OptionType::Flag,
639 description: "Check all tests",
640 conflicts_with: vec!["--test"],
641 },
642 );
643
644 options.check.insert(
645 "--bench",
646 OptionMetadata {
647 option_type: OptionType::Single,
648 description: "Check only the specified bench target",
649 conflicts_with: vec!["--benches"],
650 },
651 );
652
653 options.check.insert(
654 "--benches",
655 OptionMetadata {
656 option_type: OptionType::Flag,
657 description: "Check all benches",
658 conflicts_with: vec!["--bench"],
659 },
660 );
661
662 options.check.insert(
663 "--all-targets",
664 OptionMetadata {
665 option_type: OptionType::Flag,
666 description: "Check all targets",
667 conflicts_with: vec![],
668 },
669 );
670
671 options.bench.insert(
673 "--lib",
674 OptionMetadata {
675 option_type: OptionType::Flag,
676 description: "Benchmark only this package's library",
677 conflicts_with: vec!["--bin", "--bins"],
678 },
679 );
680
681 options.bench.insert(
682 "--bin",
683 OptionMetadata {
684 option_type: OptionType::Single,
685 description: "Benchmark only the specified binary",
686 conflicts_with: vec!["--lib", "--bins"],
687 },
688 );
689
690 options.bench.insert(
691 "--bins",
692 OptionMetadata {
693 option_type: OptionType::Flag,
694 description: "Benchmark all binaries",
695 conflicts_with: vec!["--lib", "--bin"],
696 },
697 );
698
699 options.bench.insert(
700 "--example",
701 OptionMetadata {
702 option_type: OptionType::Single,
703 description: "Benchmark only the specified example",
704 conflicts_with: vec!["--examples"],
705 },
706 );
707
708 options.bench.insert(
709 "--examples",
710 OptionMetadata {
711 option_type: OptionType::Flag,
712 description: "Benchmark all examples",
713 conflicts_with: vec!["--example"],
714 },
715 );
716
717 options.bench.insert(
718 "--test",
719 OptionMetadata {
720 option_type: OptionType::Single,
721 description: "Benchmark only the specified test target",
722 conflicts_with: vec!["--tests"],
723 },
724 );
725
726 options.bench.insert(
727 "--tests",
728 OptionMetadata {
729 option_type: OptionType::Flag,
730 description: "Benchmark all tests",
731 conflicts_with: vec!["--test"],
732 },
733 );
734
735 options.bench.insert(
736 "--bench",
737 OptionMetadata {
738 option_type: OptionType::Single,
739 description: "Benchmark only the specified bench target",
740 conflicts_with: vec!["--benches"],
741 },
742 );
743
744 options.bench.insert(
745 "--benches",
746 OptionMetadata {
747 option_type: OptionType::Flag,
748 description: "Benchmark all benches",
749 conflicts_with: vec!["--bench"],
750 },
751 );
752
753 options.bench.insert(
754 "--all-targets",
755 OptionMetadata {
756 option_type: OptionType::Flag,
757 description: "Benchmark all targets",
758 conflicts_with: vec![],
759 },
760 );
761
762 options.bench.insert(
763 "--no-run",
764 OptionMetadata {
765 option_type: OptionType::Flag,
766 description: "Compile, but don't run benchmarks",
767 conflicts_with: vec![],
768 },
769 );
770
771 options
772});
773
774impl CargoOptions {
775 pub fn get_option(&self, command: &str, option: &str) -> Option<&OptionMetadata> {
777 if let Some(metadata) = self.common.get(option) {
779 return Some(metadata);
780 }
781
782 match command {
784 "run" => self.run.get(option),
785 "test" => self.test.get(option),
786 "build" => self.build.get(option),
787 "check" => self.check.get(option),
788 "bench" => self.bench.get(option),
789 _ => None,
790 }
791 }
792
793 pub fn is_valid_option(&self, command: &str, option: &str) -> bool {
795 match command {
797 "run" | "test" | "build" | "check" | "bench" => {
798 self.get_option(command, option).is_some()
799 }
800 _ => false, }
802 }
803
804 pub fn get_all_options(&self, command: &str) -> HashSet<&'static str> {
806 let mut options: HashSet<&'static str> = self.common.keys().copied().collect();
807
808 match command {
809 "run" => options.extend(self.run.keys().copied()),
810 "test" => options.extend(self.test.keys().copied()),
811 "build" => options.extend(self.build.keys().copied()),
812 "check" => options.extend(self.check.keys().copied()),
813 "bench" => options.extend(self.bench.keys().copied()),
814 _ => {}
815 }
816
817 options
818 }
819
820 pub fn check_conflicts<'a>(
822 &self,
823 command: &str,
824 options: &[&'a str],
825 ) -> Vec<(&'a str, &'a str)> {
826 let mut conflicts = Vec::new();
827
828 for (i, &opt1) in options.iter().enumerate() {
829 if let Some(metadata) = self.get_option(command, opt1) {
830 for &opt2 in &options[i + 1..] {
831 if metadata.conflicts_with.contains(&opt2) {
832 conflicts.push((opt1, opt2));
833 }
834 }
835 }
836 }
837
838 conflicts
839 }
840}
841
842#[cfg(test)]
843mod tests {
844 use super::*;
845
846 #[test]
847 fn test_common_options() {
848 let options = &CARGO_OPTIONS;
849 assert!(options.is_valid_option("run", "--features"));
850 assert!(options.is_valid_option("test", "--release"));
851 assert!(options.is_valid_option("bench", "--package"));
852 }
853
854 #[test]
855 fn test_command_specific_options() {
856 let options = &CARGO_OPTIONS;
857 assert!(options.is_valid_option("run", "--bin"));
858 assert!(options.is_valid_option("test", "--doc"));
859 assert!(options.is_valid_option("bench", "--benches"));
860
861 assert!(!options.is_valid_option("run", "--doc")); assert!(!options.is_valid_option("run", "--no-run")); }
865
866 #[test]
867 fn test_option_conflicts() {
868 let options = &CARGO_OPTIONS;
869 let conflicts = options.check_conflicts("test", &["--lib", "--bin", "--bins"]);
870 assert!(!conflicts.is_empty());
871 assert!(conflicts.contains(&("--lib", "--bin")));
872 assert!(conflicts.contains(&("--lib", "--bins")));
873 assert!(conflicts.contains(&("--bin", "--bins")));
874 }
875}