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