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