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