1use crate::CargoResult;
2use crate::core::Dependency;
3use crate::core::compiler::{BuildConfig, CompileKind, MessageFormat, RustcTargetData};
4use crate::core::resolver::{CliFeatures, ForceAllTargets, HasDevUnits};
5use crate::core::{Edition, Package, TargetKind, Workspace, profiles::Profiles, shell};
6use crate::ops::registry::RegistryOrIndex;
7use crate::ops::{self, CompileFilter, CompileOptions, NewOptions, Packages, VersionControl};
8use crate::util::important_paths::find_root_manifest_for_wd;
9use crate::util::interning::InternedString;
10use crate::util::is_rustup;
11use crate::util::restricted_names;
12use crate::util::toml::is_embedded;
13use crate::util::{
14 print_available_benches, print_available_binaries, print_available_examples,
15 print_available_packages, print_available_tests,
16};
17use anyhow::bail;
18use cargo_util::paths;
19use cargo_util_schemas::manifest::ProfileName;
20use cargo_util_schemas::manifest::RegistryName;
21use cargo_util_schemas::manifest::StringOrVec;
22use clap::builder::UnknownArgumentValueParser;
23use clap_complete::ArgValueCandidates;
24use home::cargo_home_with_cwd;
25use indexmap::IndexSet;
26use itertools::Itertools;
27use semver::Version;
28use std::collections::{BTreeMap, HashMap, HashSet};
29use std::ffi::{OsStr, OsString};
30use std::path::Path;
31use std::path::PathBuf;
32
33pub use crate::core::compiler::UserIntent;
34pub use crate::{CliError, CliResult, GlobalContext};
35pub use clap::{Arg, ArgAction, ArgMatches, value_parser};
36
37pub use clap::Command;
38
39use super::IntoUrl;
40use super::context::JobsConfig;
41
42pub mod heading {
43 pub const PACKAGE_SELECTION: &str = "Package Selection";
44 pub const TARGET_SELECTION: &str = "Target Selection";
45 pub const FEATURE_SELECTION: &str = "Feature Selection";
46 pub const COMPILATION_OPTIONS: &str = "Compilation Options";
47 pub const MANIFEST_OPTIONS: &str = "Manifest Options";
48}
49
50pub trait CommandExt: Sized {
51 fn _arg(self, arg: Arg) -> Self;
52
53 fn arg_package_spec(
56 self,
57 package: &'static str,
58 all: &'static str,
59 exclude: &'static str,
60 ) -> Self {
61 self.arg_package_spec_no_all(
62 package,
63 all,
64 exclude,
65 ArgValueCandidates::new(get_ws_member_candidates),
66 )
67 ._arg(
68 flag("all", "Alias for --workspace (deprecated)")
69 .help_heading(heading::PACKAGE_SELECTION),
70 )
71 }
72
73 fn arg_package_spec_no_all(
77 self,
78 package: &'static str,
79 all: &'static str,
80 exclude: &'static str,
81 package_completion: ArgValueCandidates,
82 ) -> Self {
83 let unsupported_short_arg = {
84 let value_parser = UnknownArgumentValueParser::suggest_arg("--exclude");
85 Arg::new("unsupported-short-exclude-flag")
86 .help("")
87 .short('x')
88 .value_parser(value_parser)
89 .action(ArgAction::SetTrue)
90 .hide(true)
91 };
92 self.arg_package_spec_simple(package, package_completion)
93 ._arg(flag("workspace", all).help_heading(heading::PACKAGE_SELECTION))
94 ._arg(
95 multi_opt("exclude", "SPEC", exclude)
96 .help_heading(heading::PACKAGE_SELECTION)
97 .add(clap_complete::ArgValueCandidates::new(
98 get_ws_member_candidates,
99 )),
100 )
101 ._arg(unsupported_short_arg)
102 }
103
104 fn arg_package_spec_simple(
105 self,
106 package: &'static str,
107 package_completion: ArgValueCandidates,
108 ) -> Self {
109 self._arg(
110 optional_multi_opt("package", "SPEC", package)
111 .short('p')
112 .help_heading(heading::PACKAGE_SELECTION)
113 .add(package_completion),
114 )
115 }
116
117 fn arg_package(self, package: &'static str) -> Self {
118 self._arg(
119 optional_opt("package", package)
120 .short('p')
121 .value_name("SPEC")
122 .help_heading(heading::PACKAGE_SELECTION)
123 .add(clap_complete::ArgValueCandidates::new(|| {
124 get_ws_member_candidates()
125 })),
126 )
127 }
128
129 fn arg_parallel(self) -> Self {
130 self.arg_jobs()._arg(
131 flag(
132 "keep-going",
133 "Do not abort the build as soon as there is an error",
134 )
135 .help_heading(heading::COMPILATION_OPTIONS),
136 )
137 }
138
139 fn arg_jobs(self) -> Self {
140 self._arg(
141 opt("jobs", "Number of parallel jobs, defaults to # of CPUs.")
142 .short('j')
143 .value_name("N")
144 .allow_hyphen_values(true)
145 .help_heading(heading::COMPILATION_OPTIONS),
146 )
147 }
148
149 fn arg_unsupported_keep_going(self) -> Self {
150 let msg = "use `--no-fail-fast` to run as many tests as possible regardless of failure";
151 let value_parser = UnknownArgumentValueParser::suggest(msg);
152 self._arg(flag("keep-going", "").value_parser(value_parser).hide(true))
153 }
154
155 fn arg_redundant_default_mode(
156 self,
157 default_mode: &'static str,
158 command: &'static str,
159 supported_mode: &'static str,
160 ) -> Self {
161 let msg = format!(
162 "`--{default_mode}` is the default for `cargo {command}`; instead `--{supported_mode}` is supported"
163 );
164 let value_parser = UnknownArgumentValueParser::suggest(msg);
165 self._arg(
166 flag(default_mode, "")
167 .conflicts_with("profile")
168 .value_parser(value_parser)
169 .hide(true),
170 )
171 }
172
173 fn arg_targets_all(
174 self,
175 lib: &'static str,
176 bin: &'static str,
177 bins: &'static str,
178 example: &'static str,
179 examples: &'static str,
180 test: &'static str,
181 tests: &'static str,
182 bench: &'static str,
183 benches: &'static str,
184 all: &'static str,
185 ) -> Self {
186 self.arg_targets_lib_bin_example(lib, bin, bins, example, examples)
187 ._arg(flag("tests", tests).help_heading(heading::TARGET_SELECTION))
188 ._arg(
189 optional_multi_opt("test", "NAME", test)
190 .help_heading(heading::TARGET_SELECTION)
191 .add(clap_complete::ArgValueCandidates::new(|| {
192 get_crate_candidates(TargetKind::Test).unwrap_or_default()
193 })),
194 )
195 ._arg(flag("benches", benches).help_heading(heading::TARGET_SELECTION))
196 ._arg(
197 optional_multi_opt("bench", "NAME", bench)
198 .help_heading(heading::TARGET_SELECTION)
199 .add(clap_complete::ArgValueCandidates::new(|| {
200 get_crate_candidates(TargetKind::Bench).unwrap_or_default()
201 })),
202 )
203 ._arg(flag("all-targets", all).help_heading(heading::TARGET_SELECTION))
204 }
205
206 fn arg_targets_lib_bin_example(
207 self,
208 lib: &'static str,
209 bin: &'static str,
210 bins: &'static str,
211 example: &'static str,
212 examples: &'static str,
213 ) -> Self {
214 self._arg(flag("lib", lib).help_heading(heading::TARGET_SELECTION))
215 ._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION))
216 ._arg(
217 optional_multi_opt("bin", "NAME", bin)
218 .help_heading(heading::TARGET_SELECTION)
219 .add(clap_complete::ArgValueCandidates::new(|| {
220 get_crate_candidates(TargetKind::Bin).unwrap_or_default()
221 })),
222 )
223 ._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION))
224 ._arg(
225 optional_multi_opt("example", "NAME", example)
226 .help_heading(heading::TARGET_SELECTION)
227 .add(clap_complete::ArgValueCandidates::new(|| {
228 get_crate_candidates(TargetKind::ExampleBin).unwrap_or_default()
229 })),
230 )
231 }
232
233 fn arg_targets_bins_examples(
234 self,
235 bin: &'static str,
236 bins: &'static str,
237 example: &'static str,
238 examples: &'static str,
239 ) -> Self {
240 self._arg(
241 optional_multi_opt("bin", "NAME", bin)
242 .help_heading(heading::TARGET_SELECTION)
243 .add(clap_complete::ArgValueCandidates::new(|| {
244 get_crate_candidates(TargetKind::Bin).unwrap_or_default()
245 })),
246 )
247 ._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION))
248 ._arg(
249 optional_multi_opt("example", "NAME", example)
250 .help_heading(heading::TARGET_SELECTION)
251 .add(clap_complete::ArgValueCandidates::new(|| {
252 get_crate_candidates(TargetKind::ExampleBin).unwrap_or_default()
253 })),
254 )
255 ._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION))
256 }
257
258 fn arg_targets_bin_example(self, bin: &'static str, example: &'static str) -> Self {
259 self._arg(
260 optional_multi_opt("bin", "NAME", bin)
261 .help_heading(heading::TARGET_SELECTION)
262 .add(clap_complete::ArgValueCandidates::new(|| {
263 get_crate_candidates(TargetKind::Bin).unwrap_or_default()
264 })),
265 )
266 ._arg(
267 optional_multi_opt("example", "NAME", example)
268 .help_heading(heading::TARGET_SELECTION)
269 .add(clap_complete::ArgValueCandidates::new(|| {
270 get_crate_candidates(TargetKind::ExampleBin).unwrap_or_default()
271 })),
272 )
273 }
274
275 fn arg_features(self) -> Self {
276 self._arg(
277 multi_opt(
278 "features",
279 "FEATURES",
280 "Space or comma separated list of features to activate",
281 )
282 .short('F')
283 .help_heading(heading::FEATURE_SELECTION)
284 .add(clap_complete::ArgValueCandidates::new(|| {
285 get_feature_candidates().unwrap_or_default()
286 })),
287 )
288 ._arg(
289 flag("all-features", "Activate all available features")
290 .help_heading(heading::FEATURE_SELECTION),
291 )
292 ._arg(
293 flag(
294 "no-default-features",
295 "Do not activate the `default` feature",
296 )
297 .help_heading(heading::FEATURE_SELECTION),
298 )
299 }
300
301 fn arg_release(self, release: &'static str) -> Self {
302 self._arg(
303 flag("release", release)
304 .short('r')
305 .conflicts_with("profile")
306 .help_heading(heading::COMPILATION_OPTIONS),
307 )
308 }
309
310 fn arg_profile(self, profile: &'static str) -> Self {
311 self._arg(
312 opt("profile", profile)
313 .value_name("PROFILE-NAME")
314 .help_heading(heading::COMPILATION_OPTIONS)
315 .add(clap_complete::ArgValueCandidates::new(|| {
316 let candidates = get_profile_candidates();
317 candidates
318 })),
319 )
320 }
321
322 fn arg_doc(self, doc: &'static str) -> Self {
323 self._arg(flag("doc", doc))
324 }
325
326 fn arg_target_triple(self, target: &'static str) -> Self {
327 self.arg_target_triple_with_candidates(target, ArgValueCandidates::new(get_target_triples))
328 }
329
330 fn arg_target_triple_with_candidates(
331 self,
332 target: &'static str,
333 target_completion: ArgValueCandidates,
334 ) -> Self {
335 let unsupported_short_arg = {
336 let value_parser = UnknownArgumentValueParser::suggest_arg("--target");
337 Arg::new("unsupported-short-target-flag")
338 .help("")
339 .short('t')
340 .value_parser(value_parser)
341 .action(ArgAction::SetTrue)
342 .hide(true)
343 };
344 self._arg(
345 optional_multi_opt("target", "TRIPLE", target)
346 .help_heading(heading::COMPILATION_OPTIONS)
347 .add(target_completion),
348 )
349 ._arg(unsupported_short_arg)
350 }
351
352 fn arg_target_dir(self) -> Self {
353 self._arg(
354 opt("target-dir", "Directory for all generated artifacts")
355 .value_name("DIRECTORY")
356 .help_heading(heading::COMPILATION_OPTIONS),
357 )
358 }
359
360 fn arg_manifest_path(self) -> Self {
361 let unsupported_path_arg = {
363 let value_parser = UnknownArgumentValueParser::suggest_arg("--manifest-path");
364 flag("unsupported-path-flag", "")
365 .long("path")
366 .value_parser(value_parser)
367 .hide(true)
368 };
369 self.arg_manifest_path_without_unsupported_path_tip()
370 ._arg(unsupported_path_arg)
371 }
372
373 fn arg_manifest_path_without_unsupported_path_tip(self) -> Self {
375 self._arg(
376 opt("manifest-path", "Path to Cargo.toml")
377 .value_name("PATH")
378 .help_heading(heading::MANIFEST_OPTIONS)
379 .add(clap_complete::engine::ArgValueCompleter::new(
380 clap_complete::engine::PathCompleter::any().filter(|path: &Path| {
381 if path.file_name() == Some(OsStr::new("Cargo.toml")) {
382 return true;
383 }
384 if is_embedded(path) {
385 return true;
386 }
387 false
388 }),
389 )),
390 )
391 }
392
393 fn arg_message_format(self) -> Self {
394 self._arg(
395 multi_opt("message-format", "FMT", "Error format")
396 .value_parser([
397 "human",
398 "short",
399 "json",
400 "json-diagnostic-short",
401 "json-diagnostic-rendered-ansi",
402 "json-render-diagnostics",
403 ])
404 .value_delimiter(',')
405 .ignore_case(true),
406 )
407 }
408
409 fn arg_unit_graph(self) -> Self {
410 self._arg(
411 flag("unit-graph", "Output build graph in JSON (unstable)")
412 .help_heading(heading::COMPILATION_OPTIONS),
413 )
414 }
415
416 fn arg_new_opts(self) -> Self {
417 self._arg(
418 opt(
419 "vcs",
420 "Initialize a new repository for the given version \
421 control system, overriding \
422 a global configuration.",
423 )
424 .value_name("VCS")
425 .value_parser(["git", "hg", "pijul", "fossil", "none"]),
426 )
427 ._arg(flag("bin", "Use a binary (application) template [default]"))
428 ._arg(flag("lib", "Use a library template"))
429 ._arg(
430 opt("edition", "Edition to set for the crate generated")
431 .value_parser(Edition::CLI_VALUES)
432 .value_name("YEAR"),
433 )
434 ._arg(
435 opt(
436 "name",
437 "Set the resulting package name, defaults to the directory name",
438 )
439 .value_name("NAME"),
440 )
441 }
442
443 fn arg_registry(self, help: &'static str) -> Self {
444 self._arg(opt("registry", help).value_name("REGISTRY").add(
445 clap_complete::ArgValueCandidates::new(|| {
446 let candidates = get_registry_candidates();
447 candidates.unwrap_or_default()
448 }),
449 ))
450 }
451
452 fn arg_index(self, help: &'static str) -> Self {
453 self._arg(
455 opt("index", help)
456 .value_name("INDEX")
457 .conflicts_with("registry"),
458 )
459 }
460
461 fn arg_dry_run(self, dry_run: &'static str) -> Self {
462 self._arg(flag("dry-run", dry_run).short('n'))
463 }
464
465 fn arg_ignore_rust_version(self) -> Self {
466 self.arg_ignore_rust_version_with_help("Ignore `rust-version` specification in packages")
467 }
468
469 fn arg_ignore_rust_version_with_help(self, help: &'static str) -> Self {
470 self._arg(flag("ignore-rust-version", help).help_heading(heading::MANIFEST_OPTIONS))
471 }
472
473 fn arg_future_incompat_report(self) -> Self {
474 self._arg(flag(
475 "future-incompat-report",
476 "Outputs a future incompatibility report at the end of the build",
477 ))
478 }
479
480 fn arg_silent_suggestion(self) -> Self {
486 let value_parser = UnknownArgumentValueParser::suggest_arg("--quiet");
487 self._arg(
488 flag("silent", "")
489 .short('s')
490 .value_parser(value_parser)
491 .hide(true),
492 )
493 }
494
495 fn arg_timings(self) -> Self {
496 self._arg(
497 flag(
498 "timings",
499 "Output a build timing report at the end of the build",
500 )
501 .help_heading(heading::COMPILATION_OPTIONS),
502 )
503 }
504
505 fn arg_artifact_dir(self) -> Self {
506 let unsupported_short_arg = {
507 let value_parser = UnknownArgumentValueParser::suggest_arg("--artifact-dir");
508 Arg::new("unsupported-short-artifact-dir-flag")
509 .help("")
510 .short('O')
511 .value_parser(value_parser)
512 .action(ArgAction::SetTrue)
513 .hide(true)
514 };
515
516 self._arg(
517 opt(
518 "artifact-dir",
519 "Copy final artifacts to this directory (unstable)",
520 )
521 .value_name("PATH")
522 .help_heading(heading::COMPILATION_OPTIONS),
523 )
524 ._arg(unsupported_short_arg)
525 ._arg({
526 let value_parser = UnknownArgumentValueParser::suggest_arg("--artifact-dir");
527 Arg::new("unsupported-out-dir-flag")
528 .help("")
529 .long("out-dir")
530 .value_name("PATH")
531 .value_parser(value_parser)
532 .action(ArgAction::SetTrue)
533 .hide(true)
534 })
535 }
536
537 fn arg_compile_time_deps(self) -> Self {
538 self._arg(flag("compile-time-deps", "").hide(true))
539 }
540}
541
542impl CommandExt for Command {
543 fn _arg(self, arg: Arg) -> Self {
544 self.arg(arg)
545 }
546}
547
548pub fn flag(name: &'static str, help: &'static str) -> Arg {
549 Arg::new(name)
550 .long(name)
551 .help(help)
552 .action(ArgAction::SetTrue)
553}
554
555pub fn opt(name: &'static str, help: &'static str) -> Arg {
556 Arg::new(name).long(name).help(help).action(ArgAction::Set)
557}
558
559pub fn optional_opt(name: &'static str, help: &'static str) -> Arg {
560 opt(name, help).num_args(0..=1)
561}
562
563pub fn optional_multi_opt(name: &'static str, value_name: &'static str, help: &'static str) -> Arg {
564 opt(name, help)
565 .value_name(value_name)
566 .num_args(0..=1)
567 .action(ArgAction::Append)
568}
569
570pub fn multi_opt(name: &'static str, value_name: &'static str, help: &'static str) -> Arg {
571 opt(name, help)
572 .value_name(value_name)
573 .action(ArgAction::Append)
574}
575
576pub fn subcommand(name: &'static str) -> Command {
577 Command::new(name)
578}
579
580pub enum ProfileChecking {
582 LegacyRustc,
585 LegacyTestOnly,
588 Custom,
590}
591
592pub trait ArgMatchesExt {
593 fn value_of_u32(&self, name: &str) -> CargoResult<Option<u32>> {
594 let arg = match self._value_of(name) {
595 None => None,
596 Some(arg) => Some(arg.parse::<u32>().map_err(|_| {
597 clap::Error::raw(
598 clap::error::ErrorKind::ValueValidation,
599 format!("invalid value: could not parse `{}` as a number", arg),
600 )
601 })?),
602 };
603 Ok(arg)
604 }
605
606 fn value_of_i32(&self, name: &str) -> CargoResult<Option<i32>> {
607 let arg = match self._value_of(name) {
608 None => None,
609 Some(arg) => Some(arg.parse::<i32>().map_err(|_| {
610 clap::Error::raw(
611 clap::error::ErrorKind::ValueValidation,
612 format!("invalid value: could not parse `{}` as a number", arg),
613 )
614 })?),
615 };
616 Ok(arg)
617 }
618
619 fn value_of_path(&self, name: &str, gctx: &GlobalContext) -> Option<PathBuf> {
621 self._value_of(name).map(|path| gctx.cwd().join(path))
622 }
623
624 fn root_manifest(&self, gctx: &GlobalContext) -> CargoResult<PathBuf> {
625 root_manifest(self._value_of("manifest-path").map(Path::new), gctx)
626 }
627
628 #[tracing::instrument(skip_all)]
629 fn workspace<'a>(&self, gctx: &'a GlobalContext) -> CargoResult<Workspace<'a>> {
630 let root = self.root_manifest(gctx)?;
631 let mut ws = Workspace::new(&root, gctx)?;
632 ws.set_resolve_honors_rust_version(self.honor_rust_version());
633 if gctx.cli_unstable().avoid_dev_deps {
634 ws.set_require_optional_deps(false);
635 }
636 Ok(ws)
637 }
638
639 fn jobs(&self) -> CargoResult<Option<JobsConfig>> {
640 let arg = match self._value_of("jobs") {
641 None => None,
642 Some(arg) => match arg.parse::<i32>() {
643 Ok(j) => Some(JobsConfig::Integer(j)),
644 Err(_) => Some(JobsConfig::String(arg.to_string())),
645 },
646 };
647
648 Ok(arg)
649 }
650
651 fn verbose(&self) -> u32 {
652 self._count("verbose")
653 }
654
655 fn dry_run(&self) -> bool {
656 self.flag("dry-run")
657 }
658
659 fn keep_going(&self) -> bool {
660 self.maybe_flag("keep-going")
661 }
662
663 fn honor_rust_version(&self) -> Option<bool> {
664 self.flag("ignore-rust-version").then_some(false)
665 }
666
667 fn targets(&self) -> CargoResult<Vec<String>> {
668 if self.is_present_with_zero_values("target") {
669 let cmd = if is_rustup() {
670 "rustup target list"
671 } else {
672 "rustc --print target-list"
673 };
674 bail!(
675 "\"--target\" takes a target architecture as an argument.
676
677Run `{cmd}` to see possible targets."
678 );
679 }
680 Ok(self._values_of("target"))
681 }
682
683 fn get_profile_name(
684 &self,
685 default: &str,
686 profile_checking: ProfileChecking,
687 ) -> CargoResult<InternedString> {
688 let specified_profile = self._value_of("profile");
689
690 match (specified_profile, profile_checking) {
693 (Some(name @ ("dev" | "test" | "bench" | "check")), ProfileChecking::LegacyRustc)
695 | (Some(name @ "test"), ProfileChecking::LegacyTestOnly) => {
697 return Ok(name.into());
698 }
699 _ => {}
700 }
701
702 let name = match (
703 self.maybe_flag("release"),
704 self.maybe_flag("debug"),
705 specified_profile,
706 ) {
707 (false, false, None) => default,
708 (true, _, None) => "release",
709 (_, true, None) => "dev",
710 (_, _, Some("doc")) => {
716 bail!("profile `doc` is reserved and not allowed to be explicitly specified")
717 }
718 (_, _, Some(name)) => {
719 ProfileName::new(name)?;
720 name
721 }
722 };
723
724 Ok(name.into())
725 }
726
727 fn packages_from_flags(&self) -> CargoResult<Packages> {
728 Packages::from_flags(
729 self.flag("workspace") || self.flag("all"),
731 self._values_of("exclude"),
732 self._values_of("package"),
733 )
734 }
735
736 fn compile_options(
737 &self,
738 gctx: &GlobalContext,
739 intent: UserIntent,
740 workspace: Option<&Workspace<'_>>,
741 profile_checking: ProfileChecking,
742 ) -> CargoResult<CompileOptions> {
743 let spec = self.packages_from_flags()?;
744 let mut message_format = None;
745 let default_json = MessageFormat::Json {
746 short: false,
747 ansi: false,
748 render_diagnostics: false,
749 };
750 let two_kinds_of_msg_format_err = "cannot specify two kinds of `message-format` arguments";
751 for fmt in self._values_of("message-format") {
752 for fmt in fmt.split(',') {
753 let fmt = fmt.to_ascii_lowercase();
754 match fmt.as_str() {
755 "json" => {
756 if message_format.is_some() {
757 bail!(two_kinds_of_msg_format_err);
758 }
759 message_format = Some(default_json);
760 }
761 "human" => {
762 if message_format.is_some() {
763 bail!(two_kinds_of_msg_format_err);
764 }
765 message_format = Some(MessageFormat::Human);
766 }
767 "short" => {
768 if message_format.is_some() {
769 bail!(two_kinds_of_msg_format_err);
770 }
771 message_format = Some(MessageFormat::Short);
772 }
773 "json-render-diagnostics" => {
774 if message_format.is_none() {
775 message_format = Some(default_json);
776 }
777 match &mut message_format {
778 Some(MessageFormat::Json {
779 render_diagnostics, ..
780 }) => *render_diagnostics = true,
781 _ => bail!(two_kinds_of_msg_format_err),
782 }
783 }
784 "json-diagnostic-short" => {
785 if message_format.is_none() {
786 message_format = Some(default_json);
787 }
788 match &mut message_format {
789 Some(MessageFormat::Json { short, .. }) => *short = true,
790 _ => bail!(two_kinds_of_msg_format_err),
791 }
792 }
793 "json-diagnostic-rendered-ansi" => {
794 if message_format.is_none() {
795 message_format = Some(default_json);
796 }
797 match &mut message_format {
798 Some(MessageFormat::Json { ansi, .. }) => *ansi = true,
799 _ => bail!(two_kinds_of_msg_format_err),
800 }
801 }
802 s => bail!("invalid message format specifier: `{}`", s),
803 }
804 }
805 }
806
807 let mut build_config = BuildConfig::new(
808 gctx,
809 self.jobs()?,
810 self.keep_going(),
811 &self.targets()?,
812 intent,
813 )?;
814 build_config.message_format = message_format.unwrap_or(MessageFormat::Human);
815 build_config.requested_profile = self.get_profile_name("dev", profile_checking)?;
816 build_config.unit_graph = self.flag("unit-graph");
817 build_config.future_incompat_report = self.flag("future-incompat-report");
818 build_config.compile_time_deps_only = self.flag("compile-time-deps");
819 build_config.timing_report = self.flag("timings");
820
821 if build_config.unit_graph {
822 gctx.cli_unstable()
823 .fail_if_stable_opt("--unit-graph", 8002)?;
824 }
825 if build_config.compile_time_deps_only {
826 gctx.cli_unstable()
827 .fail_if_stable_opt("--compile-time-deps", 14434)?;
828 }
829
830 let opts = CompileOptions {
831 build_config,
832 cli_features: self.cli_features()?,
833 spec,
834 filter: CompileFilter::from_raw_arguments(
835 self.flag("lib"),
836 self._values_of("bin"),
837 self.flag("bins"),
838 self._values_of("test"),
839 self.flag("tests"),
840 self._values_of("example"),
841 self.flag("examples"),
842 self._values_of("bench"),
843 self.flag("benches"),
844 self.flag("all-targets"),
845 ),
846 target_rustdoc_args: None,
847 target_rustc_args: None,
848 target_rustc_crate_types: None,
849 rustdoc_document_private_items: false,
850 honor_rust_version: self.honor_rust_version(),
851 };
852
853 if let Some(ws) = workspace {
854 self.check_optional_opts(ws, &opts)?;
855 } else if self.is_present_with_zero_values("package") {
856 anyhow::bail!(
859 "\"--package <SPEC>\" requires a SPEC format value, \
860 which can be any package ID specifier in the dependency graph.\n\
861 Run `cargo help pkgid` for more information about SPEC format."
862 )
863 }
864
865 Ok(opts)
866 }
867
868 fn cli_features(&self) -> CargoResult<CliFeatures> {
869 CliFeatures::from_command_line(
870 &self._values_of("features"),
871 self.flag("all-features"),
872 !self.flag("no-default-features"),
873 )
874 }
875
876 fn compile_options_for_single_package(
877 &self,
878 gctx: &GlobalContext,
879 intent: UserIntent,
880 workspace: Option<&Workspace<'_>>,
881 profile_checking: ProfileChecking,
882 ) -> CargoResult<CompileOptions> {
883 let mut compile_opts = self.compile_options(gctx, intent, workspace, profile_checking)?;
884 let spec = self._values_of("package");
885 if spec.iter().any(restricted_names::is_glob_pattern) {
886 anyhow::bail!("glob patterns on package selection are not supported.")
887 }
888 compile_opts.spec = Packages::Packages(spec);
889 Ok(compile_opts)
890 }
891
892 fn new_options(&self, gctx: &GlobalContext) -> CargoResult<NewOptions> {
893 let vcs = self._value_of("vcs").map(|vcs| match vcs {
894 "git" => VersionControl::Git,
895 "hg" => VersionControl::Hg,
896 "pijul" => VersionControl::Pijul,
897 "fossil" => VersionControl::Fossil,
898 "none" => VersionControl::NoVcs,
899 vcs => panic!("Impossible vcs: {:?}", vcs),
900 });
901 NewOptions::new(
902 vcs,
903 self.flag("bin"),
904 self.flag("lib"),
905 self.value_of_path("path", gctx).unwrap(),
906 self._value_of("name").map(|s| s.to_string()),
907 self._value_of("edition").map(|s| s.to_string()),
908 self.registry(gctx)?,
909 )
910 }
911
912 fn registry_or_index(&self, gctx: &GlobalContext) -> CargoResult<Option<RegistryOrIndex>> {
913 let registry = self._value_of("registry");
914 let index = self._value_of("index");
915 let result = match (registry, index) {
916 (None, None) => gctx.default_registry()?.map(RegistryOrIndex::Registry),
917 (None, Some(i)) => Some(RegistryOrIndex::Index(i.into_url()?)),
918 (Some(r), None) => {
919 RegistryName::new(r)?;
920 Some(RegistryOrIndex::Registry(r.to_string()))
921 }
922 (Some(_), Some(_)) => {
923 unreachable!("both `--index` and `--registry` should not be set at the same time")
925 }
926 };
927 Ok(result)
928 }
929
930 fn registry(&self, gctx: &GlobalContext) -> CargoResult<Option<String>> {
931 match self._value_of("registry").map(|s| s.to_string()) {
932 None => gctx.default_registry(),
933 Some(registry) => {
934 RegistryName::new(®istry)?;
935 Ok(Some(registry))
936 }
937 }
938 }
939
940 fn check_optional_opts(
941 &self,
942 workspace: &Workspace<'_>,
943 compile_opts: &CompileOptions,
944 ) -> CargoResult<()> {
945 if self.is_present_with_zero_values("package") {
946 print_available_packages(workspace)?
947 }
948
949 if self.is_present_with_zero_values("example") {
950 print_available_examples(workspace, compile_opts)?;
951 }
952
953 if self.is_present_with_zero_values("bin") {
954 print_available_binaries(workspace, compile_opts)?;
955 }
956
957 if self.is_present_with_zero_values("bench") {
958 print_available_benches(workspace, compile_opts)?;
959 }
960
961 if self.is_present_with_zero_values("test") {
962 print_available_tests(workspace, compile_opts)?;
963 }
964
965 Ok(())
966 }
967
968 fn is_present_with_zero_values(&self, name: &str) -> bool {
969 self._contains(name) && self._value_of(name).is_none()
970 }
971
972 fn flag(&self, name: &str) -> bool;
973
974 fn maybe_flag(&self, name: &str) -> bool;
975
976 fn _value_of(&self, name: &str) -> Option<&str>;
977
978 fn _values_of(&self, name: &str) -> Vec<String>;
979
980 fn _value_of_os(&self, name: &str) -> Option<&OsStr>;
981
982 fn _values_of_os(&self, name: &str) -> Vec<OsString>;
983
984 fn _count(&self, name: &str) -> u32;
985
986 fn _contains(&self, name: &str) -> bool;
987}
988
989impl<'a> ArgMatchesExt for ArgMatches {
990 fn flag(&self, name: &str) -> bool {
991 ignore_unknown(self.try_get_one::<bool>(name))
992 .copied()
993 .unwrap_or(false)
994 }
995
996 fn maybe_flag(&self, name: &str) -> bool {
1000 self.try_get_one::<bool>(name)
1001 .ok()
1002 .flatten()
1003 .copied()
1004 .unwrap_or_default()
1005 }
1006
1007 fn _value_of(&self, name: &str) -> Option<&str> {
1008 ignore_unknown(self.try_get_one::<String>(name)).map(String::as_str)
1009 }
1010
1011 fn _value_of_os(&self, name: &str) -> Option<&OsStr> {
1012 ignore_unknown(self.try_get_one::<OsString>(name)).map(OsString::as_os_str)
1013 }
1014
1015 fn _values_of(&self, name: &str) -> Vec<String> {
1016 ignore_unknown(self.try_get_many::<String>(name))
1017 .unwrap_or_default()
1018 .cloned()
1019 .collect()
1020 }
1021
1022 fn _values_of_os(&self, name: &str) -> Vec<OsString> {
1023 ignore_unknown(self.try_get_many::<OsString>(name))
1024 .unwrap_or_default()
1025 .cloned()
1026 .collect()
1027 }
1028
1029 fn _count(&self, name: &str) -> u32 {
1030 *ignore_unknown(self.try_get_one::<u8>(name)).expect("defaulted by clap") as u32
1031 }
1032
1033 fn _contains(&self, name: &str) -> bool {
1034 ignore_unknown(self.try_contains_id(name))
1035 }
1036}
1037
1038pub fn values(args: &ArgMatches, name: &str) -> Vec<String> {
1039 args._values_of(name)
1040}
1041
1042pub fn values_os(args: &ArgMatches, name: &str) -> Vec<OsString> {
1043 args._values_of_os(name)
1044}
1045
1046pub fn root_manifest(manifest_path: Option<&Path>, gctx: &GlobalContext) -> CargoResult<PathBuf> {
1047 if let Some(manifest_path) = manifest_path {
1048 let path = gctx.cwd().join(manifest_path);
1049 let path = paths::normalize_path(&path);
1052 if !path.exists() {
1053 anyhow::bail!("manifest path `{}` does not exist", manifest_path.display())
1054 } else if path.is_dir() {
1055 let child_path = path.join("Cargo.toml");
1056 let suggested_path = if child_path.exists() {
1057 format!("\nhelp: {} exists", child_path.display())
1058 } else {
1059 "".to_string()
1060 };
1061 anyhow::bail!(
1062 "manifest path `{}` is a directory but expected a file{suggested_path}",
1063 manifest_path.display()
1064 )
1065 } else if !path.ends_with("Cargo.toml") && !crate::util::toml::is_embedded(&path) {
1066 if gctx.cli_unstable().script {
1067 anyhow::bail!(
1068 "the manifest-path must be a path to a Cargo.toml or script file: `{}`",
1069 path.display()
1070 )
1071 } else {
1072 anyhow::bail!(
1073 "the manifest-path must be a path to a Cargo.toml file: `{}`",
1074 path.display()
1075 )
1076 }
1077 }
1078 if crate::util::toml::is_embedded(&path) && !gctx.cli_unstable().script {
1079 anyhow::bail!("embedded manifest `{}` requires `-Zscript`", path.display())
1080 }
1081 Ok(path)
1082 } else {
1083 find_root_manifest_for_wd(gctx.cwd())
1084 }
1085}
1086
1087pub fn get_registry_candidates() -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1088 let gctx = new_gctx_for_completions()?;
1089
1090 if let Ok(Some(registries)) =
1091 gctx.get::<Option<HashMap<String, HashMap<String, String>>>>("registries")
1092 {
1093 Ok(registries
1094 .keys()
1095 .map(|name| clap_complete::CompletionCandidate::new(name.to_owned()))
1096 .collect())
1097 } else {
1098 Ok(vec![])
1099 }
1100}
1101
1102fn get_profile_candidates() -> Vec<clap_complete::CompletionCandidate> {
1103 match get_workspace_profile_candidates() {
1104 Ok(candidates) if !candidates.is_empty() => candidates,
1105 _ => default_profile_candidates(),
1107 }
1108}
1109
1110fn get_workspace_profile_candidates() -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1111 let gctx = new_gctx_for_completions()?;
1112 let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?;
1113 let profiles = Profiles::new(&ws, "dev".into())?;
1114
1115 let mut candidates = Vec::new();
1116 for name in profiles.profile_names() {
1117 let Ok(profile_instance) = Profiles::new(&ws, name) else {
1118 continue;
1119 };
1120 let base_profile = profile_instance.base_profile();
1121
1122 let mut description = String::from(if base_profile.opt_level.as_str() == "0" {
1123 "unoptimized"
1124 } else {
1125 "optimized"
1126 });
1127
1128 if base_profile.debuginfo.is_turned_on() {
1129 description.push_str(" + debuginfo");
1130 }
1131
1132 candidates
1133 .push(clap_complete::CompletionCandidate::new(&name).help(Some(description.into())));
1134 }
1135
1136 Ok(candidates)
1137}
1138
1139fn default_profile_candidates() -> Vec<clap_complete::CompletionCandidate> {
1140 vec![
1141 clap_complete::CompletionCandidate::new("dev").help(Some("unoptimized + debuginfo".into())),
1142 clap_complete::CompletionCandidate::new("release").help(Some("optimized".into())),
1143 clap_complete::CompletionCandidate::new("test")
1144 .help(Some("unoptimized + debuginfo".into())),
1145 clap_complete::CompletionCandidate::new("bench").help(Some("optimized".into())),
1146 ]
1147}
1148
1149fn get_feature_candidates() -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1150 let gctx = new_gctx_for_completions()?;
1151
1152 let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?;
1153 let mut feature_candidates = Vec::new();
1154
1155 for package in ws.members() {
1157 let package_name = package.name();
1158
1159 for feature_name in package.summary().features().keys() {
1161 let order = if ws.current_opt().map(|p| p.name()) == Some(package_name) {
1162 0
1163 } else {
1164 1
1165 };
1166 feature_candidates.push(
1167 clap_complete::CompletionCandidate::new(feature_name)
1168 .display_order(Some(order))
1169 .help(Some(format!("from {}", package_name).into())),
1170 );
1171 }
1172 }
1173
1174 Ok(feature_candidates)
1175}
1176
1177fn get_crate_candidates(kind: TargetKind) -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1178 let gctx = new_gctx_for_completions()?;
1179
1180 let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?;
1181
1182 let targets = ws
1183 .members()
1184 .flat_map(|pkg| pkg.targets().into_iter().cloned().map(|t| (pkg.name(), t)))
1185 .filter(|(_, target)| *target.kind() == kind)
1186 .map(|(pkg_name, target)| {
1187 let order = if ws.current_opt().map(|p| p.name()) == Some(pkg_name) {
1188 0
1189 } else {
1190 1
1191 };
1192 clap_complete::CompletionCandidate::new(target.name())
1193 .display_order(Some(order))
1194 .help(Some(format!("from {}", pkg_name).into()))
1195 })
1196 .collect::<Vec<_>>();
1197
1198 Ok(targets)
1199}
1200
1201fn get_target_triples() -> Vec<clap_complete::CompletionCandidate> {
1202 let mut candidates = Vec::new();
1203
1204 if let Ok(targets) = get_target_triples_from_rustup() {
1205 candidates = targets;
1206 }
1207
1208 if candidates.is_empty() {
1209 if let Ok(targets) = get_target_triples_from_rustc() {
1210 candidates = targets;
1211 }
1212 }
1213
1214 candidates.insert(
1216 0,
1217 clap_complete::CompletionCandidate::new("host-tuple").help(Some(
1218 concat!("alias for: ", env!("RUST_HOST_TARGET")).into(),
1219 )),
1220 );
1221
1222 candidates
1223}
1224
1225pub fn get_target_triples_with_all() -> Vec<clap_complete::CompletionCandidate> {
1226 let mut candidates = vec![
1227 clap_complete::CompletionCandidate::new("all").help(Some("Include all targets".into())),
1228 ];
1229 candidates.extend(get_target_triples());
1230 candidates
1231}
1232
1233fn get_target_triples_from_rustup() -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1234 let output = std::process::Command::new("rustup")
1235 .arg("target")
1236 .arg("list")
1237 .output()?;
1238
1239 if !output.status.success() {
1240 return Ok(vec![]);
1241 }
1242
1243 let stdout = String::from_utf8(output.stdout)?;
1244
1245 Ok(stdout
1246 .lines()
1247 .map(|line| {
1248 let target = line.split_once(' ');
1249 match target {
1250 None => clap_complete::CompletionCandidate::new(line.to_owned()).hide(true),
1251 Some((target, _installed)) => clap_complete::CompletionCandidate::new(target),
1252 }
1253 })
1254 .collect())
1255}
1256
1257fn get_target_triples_from_rustc() -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1258 let gctx = new_gctx_for_completions()?;
1259
1260 let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx);
1261
1262 let rustc = gctx.load_global_rustc(ws.as_ref().ok())?;
1263
1264 let (stdout, _stderr) =
1265 rustc.cached_output(rustc.process().arg("--print").arg("target-list"), 0)?;
1266
1267 Ok(stdout
1268 .lines()
1269 .map(|line| clap_complete::CompletionCandidate::new(line.to_owned()))
1270 .collect())
1271}
1272
1273pub fn get_ws_member_candidates() -> Vec<clap_complete::CompletionCandidate> {
1274 get_ws_member_packages()
1275 .unwrap_or_default()
1276 .into_iter()
1277 .map(|pkg| {
1278 clap_complete::CompletionCandidate::new(pkg.name().as_str()).help(
1279 pkg.manifest()
1280 .metadata()
1281 .description
1282 .to_owned()
1283 .map(From::from),
1284 )
1285 })
1286 .collect::<Vec<_>>()
1287}
1288
1289fn get_ws_member_packages() -> CargoResult<Vec<Package>> {
1290 let gctx = new_gctx_for_completions()?;
1291 let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?;
1292 let packages = ws.members().map(Clone::clone).collect::<Vec<_>>();
1293 Ok(packages)
1294}
1295
1296pub fn get_pkg_id_spec_candidates() -> Vec<clap_complete::CompletionCandidate> {
1297 let mut candidates = vec![];
1298
1299 let package_map = HashMap::<&str, Vec<Package>>::new();
1300 let package_map =
1301 get_packages()
1302 .unwrap_or_default()
1303 .into_iter()
1304 .fold(package_map, |mut map, package| {
1305 map.entry(package.name().as_str())
1306 .or_insert_with(Vec::new)
1307 .push(package);
1308 map
1309 });
1310
1311 let unique_name_candidates = package_map
1312 .iter()
1313 .filter(|(_name, packages)| packages.len() == 1)
1314 .map(|(name, packages)| {
1315 clap_complete::CompletionCandidate::new(name.to_string()).help(
1316 packages[0]
1317 .manifest()
1318 .metadata()
1319 .description
1320 .to_owned()
1321 .map(From::from),
1322 )
1323 })
1324 .collect::<Vec<_>>();
1325
1326 let duplicate_name_pairs = package_map
1327 .iter()
1328 .filter(|(_name, packages)| packages.len() > 1)
1329 .collect::<Vec<_>>();
1330
1331 let mut duplicate_name_candidates = vec![];
1332 for (name, packages) in duplicate_name_pairs {
1333 let mut version_count: HashMap<&Version, usize> = HashMap::new();
1334
1335 for package in packages {
1336 *version_count.entry(package.version()).or_insert(0) += 1;
1337 }
1338
1339 for package in packages {
1340 if let Some(&count) = version_count.get(package.version()) {
1341 if count == 1 {
1342 duplicate_name_candidates.push(
1343 clap_complete::CompletionCandidate::new(format!(
1344 "{}@{}",
1345 name,
1346 package.version()
1347 ))
1348 .help(
1349 package
1350 .manifest()
1351 .metadata()
1352 .description
1353 .to_owned()
1354 .map(From::from),
1355 ),
1356 );
1357 } else {
1358 duplicate_name_candidates.push(
1359 clap_complete::CompletionCandidate::new(format!(
1360 "{}",
1361 package.package_id().to_spec()
1362 ))
1363 .help(
1364 package
1365 .manifest()
1366 .metadata()
1367 .description
1368 .to_owned()
1369 .map(From::from),
1370 ),
1371 )
1372 }
1373 }
1374 }
1375 }
1376
1377 candidates.extend(unique_name_candidates);
1378 candidates.extend(duplicate_name_candidates);
1379
1380 candidates
1381}
1382
1383pub fn get_pkg_name_candidates() -> Vec<clap_complete::CompletionCandidate> {
1384 let packages: BTreeMap<_, _> = get_packages()
1385 .unwrap_or_default()
1386 .into_iter()
1387 .map(|package| {
1388 (
1389 package.name(),
1390 package.manifest().metadata().description.clone(),
1391 )
1392 })
1393 .collect();
1394
1395 packages
1396 .into_iter()
1397 .map(|(name, description)| {
1398 clap_complete::CompletionCandidate::new(name.as_str()).help(description.map(From::from))
1399 })
1400 .collect()
1401}
1402
1403fn get_packages() -> CargoResult<Vec<Package>> {
1404 let gctx = new_gctx_for_completions()?;
1405
1406 let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?;
1407
1408 let requested_kinds = CompileKind::from_requested_targets(ws.gctx(), &[])?;
1409 let mut target_data = RustcTargetData::new(&ws, &requested_kinds)?;
1410 let cli_features = CliFeatures::new_all(true);
1412 let has_dev_units = HasDevUnits::Yes;
1413 let force_all_targets = ForceAllTargets::No;
1414 let dry_run = true;
1415
1416 let ws_resolve = ops::resolve_ws_with_opts(
1417 &ws,
1418 &mut target_data,
1419 &requested_kinds,
1420 &cli_features,
1421 &[],
1422 has_dev_units,
1423 force_all_targets,
1424 dry_run,
1425 )?;
1426
1427 let packages = ws_resolve
1428 .pkg_set
1429 .packages()
1430 .map(Clone::clone)
1431 .collect::<Vec<_>>();
1432
1433 Ok(packages)
1434}
1435
1436pub fn get_direct_dependencies_pkg_name_candidates() -> Vec<clap_complete::CompletionCandidate> {
1437 let (current_package_deps, all_package_deps) = match get_dependencies_from_metadata() {
1438 Ok(v) => v,
1439 Err(_) => return Vec::new(),
1440 };
1441
1442 let current_package_deps_package_names = current_package_deps
1443 .into_iter()
1444 .map(|dep| dep.package_name().to_string())
1445 .sorted();
1446 let all_package_deps_package_names = all_package_deps
1447 .into_iter()
1448 .map(|dep| dep.package_name().to_string())
1449 .sorted();
1450
1451 let mut package_names_set = IndexSet::new();
1452 package_names_set.extend(current_package_deps_package_names);
1453 package_names_set.extend(all_package_deps_package_names);
1454
1455 package_names_set
1456 .into_iter()
1457 .map(|name| name.into())
1458 .collect_vec()
1459}
1460
1461fn get_dependencies_from_metadata() -> CargoResult<(Vec<Dependency>, Vec<Dependency>)> {
1462 let cwd = std::env::current_dir()?;
1463 let gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?);
1464 let ws = Workspace::new(&find_root_manifest_for_wd(&cwd)?, &gctx)?;
1465 let current_package = ws.current().ok();
1466
1467 let current_package_dependencies = ws
1468 .current()
1469 .map(|current| current.dependencies())
1470 .unwrap_or_default()
1471 .to_vec();
1472 let all_other_packages_dependencies = ws
1473 .members()
1474 .filter(|&member| Some(member) != current_package)
1475 .flat_map(|pkg| pkg.dependencies().into_iter().cloned())
1476 .collect::<HashSet<_>>()
1477 .into_iter()
1478 .collect::<Vec<_>>();
1479
1480 Ok((
1481 current_package_dependencies,
1482 all_other_packages_dependencies,
1483 ))
1484}
1485
1486pub fn new_gctx_for_completions() -> CargoResult<GlobalContext> {
1487 let cwd = std::env::current_dir()?;
1488 let mut gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?);
1489
1490 let verbose = 0;
1491 let quiet = true;
1492 let color = None;
1493 let frozen = false;
1494 let locked = true;
1495 let offline = false;
1496 let target_dir = None;
1497 let unstable_flags = &[];
1498 let cli_config = &[];
1499
1500 gctx.configure(
1501 verbose,
1502 quiet,
1503 color,
1504 frozen,
1505 locked,
1506 offline,
1507 &target_dir,
1508 unstable_flags,
1509 cli_config,
1510 )?;
1511
1512 Ok(gctx)
1513}
1514
1515#[track_caller]
1516pub fn ignore_unknown<T: Default>(r: Result<T, clap::parser::MatchesError>) -> T {
1517 match r {
1518 Ok(t) => t,
1519 Err(clap::parser::MatchesError::UnknownArgument { .. }) => Default::default(),
1520 Err(e) => {
1521 panic!("Mismatch between definition and access: {}", e);
1522 }
1523 }
1524}
1525
1526#[derive(PartialEq, Eq, PartialOrd, Ord)]
1527pub enum CommandInfo {
1528 BuiltIn { about: Option<String> },
1529 External { path: PathBuf },
1530 Alias { target: StringOrVec },
1531}