1use crate::CargoResult;
2use crate::core::Dependency;
3use crate::core::compiler::{BuildConfig, CompileKind, MessageFormat, RustcTargetData};
4use crate::core::resolver::{CliFeatures, ForceAllTargets, HasDevUnits};
5use crate::core::{Edition, Package, TargetKind, Workspace, profiles::Profiles, shell};
6use crate::ops::lockfile::LOCKFILE_NAME;
7use crate::ops::registry::RegistryOrIndex;
8use crate::ops::{self, CompileFilter, CompileOptions, NewOptions, Packages, VersionControl};
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::toml::is_embedded;
14use crate::util::{
15 print_available_benches, print_available_binaries, print_available_examples,
16 print_available_packages, print_available_tests,
17};
18use anyhow::bail;
19use cargo_util::paths;
20use cargo_util_schemas::manifest::ProfileName;
21use cargo_util_schemas::manifest::RegistryName;
22use cargo_util_schemas::manifest::StringOrVec;
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 .value_name("PATH")
379 .help_heading(heading::MANIFEST_OPTIONS)
380 .add(clap_complete::engine::ArgValueCompleter::new(
381 clap_complete::engine::PathCompleter::any().filter(|path: &Path| {
382 if path.file_name() == Some(OsStr::new("Cargo.toml")) {
383 return true;
384 }
385 if is_embedded(path) {
386 return true;
387 }
388 false
389 }),
390 )),
391 )
392 }
393
394 fn arg_lockfile_path(self) -> Self {
395 self._arg(
396 opt("lockfile-path", "Path to Cargo.lock (unstable)")
397 .value_name("PATH")
398 .help_heading(heading::MANIFEST_OPTIONS)
399 .add(clap_complete::engine::ArgValueCompleter::new(
400 clap_complete::engine::PathCompleter::any().filter(|path: &Path| {
401 let file_name = match path.file_name() {
402 Some(name) => name,
403 None => return false,
404 };
405
406 file_name == OsStr::new("Cargo.lock")
408 }),
409 )),
410 )
411 }
412
413 fn arg_message_format(self) -> Self {
414 self._arg(
415 multi_opt("message-format", "FMT", "Error format")
416 .value_parser([
417 "human",
418 "short",
419 "json",
420 "json-diagnostic-short",
421 "json-diagnostic-rendered-ansi",
422 "json-render-diagnostics",
423 ])
424 .value_delimiter(',')
425 .ignore_case(true),
426 )
427 }
428
429 fn arg_unit_graph(self) -> Self {
430 self._arg(
431 flag("unit-graph", "Output build graph in JSON (unstable)")
432 .help_heading(heading::COMPILATION_OPTIONS),
433 )
434 }
435
436 fn arg_new_opts(self) -> Self {
437 self._arg(
438 opt(
439 "vcs",
440 "Initialize a new repository for the given version \
441 control system, overriding \
442 a global configuration.",
443 )
444 .value_name("VCS")
445 .value_parser(["git", "hg", "pijul", "fossil", "none"]),
446 )
447 ._arg(flag("bin", "Use a binary (application) template [default]"))
448 ._arg(flag("lib", "Use a library template"))
449 ._arg(
450 opt("edition", "Edition to set for the crate generated")
451 .value_parser(Edition::CLI_VALUES)
452 .value_name("YEAR"),
453 )
454 ._arg(
455 opt(
456 "name",
457 "Set the resulting package name, defaults to the directory name",
458 )
459 .value_name("NAME"),
460 )
461 }
462
463 fn arg_registry(self, help: &'static str) -> Self {
464 self._arg(opt("registry", help).value_name("REGISTRY").add(
465 clap_complete::ArgValueCandidates::new(|| {
466 let candidates = get_registry_candidates();
467 candidates.unwrap_or_default()
468 }),
469 ))
470 }
471
472 fn arg_index(self, help: &'static str) -> Self {
473 self._arg(
475 opt("index", help)
476 .value_name("INDEX")
477 .conflicts_with("registry"),
478 )
479 }
480
481 fn arg_dry_run(self, dry_run: &'static str) -> Self {
482 self._arg(flag("dry-run", dry_run).short('n'))
483 }
484
485 fn arg_ignore_rust_version(self) -> Self {
486 self.arg_ignore_rust_version_with_help("Ignore `rust-version` specification in packages")
487 }
488
489 fn arg_ignore_rust_version_with_help(self, help: &'static str) -> Self {
490 self._arg(flag("ignore-rust-version", help).help_heading(heading::MANIFEST_OPTIONS))
491 }
492
493 fn arg_future_incompat_report(self) -> Self {
494 self._arg(flag(
495 "future-incompat-report",
496 "Outputs a future incompatibility report at the end of the build",
497 ))
498 }
499
500 fn arg_silent_suggestion(self) -> Self {
506 let value_parser = UnknownArgumentValueParser::suggest_arg("--quiet");
507 self._arg(
508 flag("silent", "")
509 .short('s')
510 .value_parser(value_parser)
511 .hide(true),
512 )
513 }
514
515 fn arg_timings(self) -> Self {
516 self._arg(
517 flag(
518 "timings",
519 "Output a build timing report at the end of the build",
520 )
521 .help_heading(heading::COMPILATION_OPTIONS),
522 )
523 }
524
525 fn arg_artifact_dir(self) -> Self {
526 let unsupported_short_arg = {
527 let value_parser = UnknownArgumentValueParser::suggest_arg("--artifact-dir");
528 Arg::new("unsupported-short-artifact-dir-flag")
529 .help("")
530 .short('O')
531 .value_parser(value_parser)
532 .action(ArgAction::SetTrue)
533 .hide(true)
534 };
535
536 self._arg(
537 opt(
538 "artifact-dir",
539 "Copy final artifacts to this directory (unstable)",
540 )
541 .value_name("PATH")
542 .help_heading(heading::COMPILATION_OPTIONS),
543 )
544 ._arg(unsupported_short_arg)
545 ._arg(
546 opt(
547 "out-dir",
548 "Copy final artifacts to this directory (deprecated; use --artifact-dir instead)",
549 )
550 .value_name("PATH")
551 .conflicts_with("artifact-dir")
552 .hide(true),
553 )
554 }
555
556 fn arg_compile_time_deps(self) -> Self {
557 self._arg(flag("compile-time-deps", "").hide(true))
558 }
559}
560
561impl CommandExt for Command {
562 fn _arg(self, arg: Arg) -> Self {
563 self.arg(arg)
564 }
565}
566
567pub fn flag(name: &'static str, help: &'static str) -> Arg {
568 Arg::new(name)
569 .long(name)
570 .help(help)
571 .action(ArgAction::SetTrue)
572}
573
574pub fn opt(name: &'static str, help: &'static str) -> Arg {
575 Arg::new(name).long(name).help(help).action(ArgAction::Set)
576}
577
578pub fn optional_opt(name: &'static str, help: &'static str) -> Arg {
579 opt(name, help).num_args(0..=1)
580}
581
582pub fn optional_multi_opt(name: &'static str, value_name: &'static str, help: &'static str) -> Arg {
583 opt(name, help)
584 .value_name(value_name)
585 .num_args(0..=1)
586 .action(ArgAction::Append)
587}
588
589pub fn multi_opt(name: &'static str, value_name: &'static str, help: &'static str) -> Arg {
590 opt(name, help)
591 .value_name(value_name)
592 .action(ArgAction::Append)
593}
594
595pub fn subcommand(name: &'static str) -> Command {
596 Command::new(name)
597}
598
599pub enum ProfileChecking {
601 LegacyRustc,
604 LegacyTestOnly,
607 Custom,
609}
610
611pub trait ArgMatchesExt {
612 fn value_of_u32(&self, name: &str) -> CargoResult<Option<u32>> {
613 let arg = match self._value_of(name) {
614 None => None,
615 Some(arg) => Some(arg.parse::<u32>().map_err(|_| {
616 clap::Error::raw(
617 clap::error::ErrorKind::ValueValidation,
618 format!("Invalid value: could not parse `{}` as a number", arg),
619 )
620 })?),
621 };
622 Ok(arg)
623 }
624
625 fn value_of_i32(&self, name: &str) -> CargoResult<Option<i32>> {
626 let arg = match self._value_of(name) {
627 None => None,
628 Some(arg) => Some(arg.parse::<i32>().map_err(|_| {
629 clap::Error::raw(
630 clap::error::ErrorKind::ValueValidation,
631 format!("Invalid value: could not parse `{}` as a number", arg),
632 )
633 })?),
634 };
635 Ok(arg)
636 }
637
638 fn value_of_path(&self, name: &str, gctx: &GlobalContext) -> Option<PathBuf> {
640 self._value_of(name).map(|path| gctx.cwd().join(path))
641 }
642
643 fn root_manifest(&self, gctx: &GlobalContext) -> CargoResult<PathBuf> {
644 root_manifest(self._value_of("manifest-path").map(Path::new), gctx)
645 }
646
647 fn lockfile_path(&self, gctx: &GlobalContext) -> CargoResult<Option<PathBuf>> {
648 lockfile_path(self._value_of("lockfile-path").map(Path::new), gctx)
649 }
650
651 #[tracing::instrument(skip_all)]
652 fn workspace<'a>(&self, gctx: &'a GlobalContext) -> CargoResult<Workspace<'a>> {
653 let root = self.root_manifest(gctx)?;
654 let lockfile_path = self.lockfile_path(gctx)?;
655 let mut ws = Workspace::new(&root, gctx)?;
656 ws.set_resolve_honors_rust_version(self.honor_rust_version());
657 if gctx.cli_unstable().avoid_dev_deps {
658 ws.set_require_optional_deps(false);
659 }
660 if lockfile_path.is_some() {
661 if ws.requested_lockfile_path().is_some() {
662 gctx.shell().warn(
663 "`--lockfile-path` is ignored because `resolver.lockfile-path` is set in config",
664 )?;
665 } else {
666 ws.set_requested_lockfile_path(lockfile_path);
667 }
668 }
669 Ok(ws)
670 }
671
672 fn jobs(&self) -> CargoResult<Option<JobsConfig>> {
673 let arg = match self._value_of("jobs") {
674 None => None,
675 Some(arg) => match arg.parse::<i32>() {
676 Ok(j) => Some(JobsConfig::Integer(j)),
677 Err(_) => Some(JobsConfig::String(arg.to_string())),
678 },
679 };
680
681 Ok(arg)
682 }
683
684 fn verbose(&self) -> u32 {
685 self._count("verbose")
686 }
687
688 fn dry_run(&self) -> bool {
689 self.flag("dry-run")
690 }
691
692 fn keep_going(&self) -> bool {
693 self.maybe_flag("keep-going")
694 }
695
696 fn honor_rust_version(&self) -> Option<bool> {
697 self.flag("ignore-rust-version").then_some(false)
698 }
699
700 fn targets(&self) -> CargoResult<Vec<String>> {
701 if self.is_present_with_zero_values("target") {
702 let cmd = if is_rustup() {
703 "rustup target list"
704 } else {
705 "rustc --print target-list"
706 };
707 bail!(
708 "\"--target\" takes a target architecture as an argument.
709
710Run `{cmd}` to see possible targets."
711 );
712 }
713 Ok(self._values_of("target"))
714 }
715
716 fn get_profile_name(
717 &self,
718 default: &str,
719 profile_checking: ProfileChecking,
720 ) -> CargoResult<InternedString> {
721 let specified_profile = self._value_of("profile");
722
723 match (specified_profile, profile_checking) {
726 (Some(name @ ("dev" | "test" | "bench" | "check")), ProfileChecking::LegacyRustc)
728 | (Some(name @ "test"), ProfileChecking::LegacyTestOnly) => {
730 return Ok(name.into());
731 }
732 _ => {}
733 }
734
735 let name = match (
736 self.maybe_flag("release"),
737 self.maybe_flag("debug"),
738 specified_profile,
739 ) {
740 (false, false, None) => default,
741 (true, _, None) => "release",
742 (_, true, None) => "dev",
743 (_, _, Some("doc")) => {
749 bail!("profile `doc` is reserved and not allowed to be explicitly specified")
750 }
751 (_, _, Some(name)) => {
752 ProfileName::new(name)?;
753 name
754 }
755 };
756
757 Ok(name.into())
758 }
759
760 fn packages_from_flags(&self) -> CargoResult<Packages> {
761 Packages::from_flags(
762 self.flag("workspace") || self.flag("all"),
764 self._values_of("exclude"),
765 self._values_of("package"),
766 )
767 }
768
769 fn compile_options(
770 &self,
771 gctx: &GlobalContext,
772 intent: UserIntent,
773 workspace: Option<&Workspace<'_>>,
774 profile_checking: ProfileChecking,
775 ) -> CargoResult<CompileOptions> {
776 let spec = self.packages_from_flags()?;
777 let mut message_format = None;
778 let default_json = MessageFormat::Json {
779 short: false,
780 ansi: false,
781 render_diagnostics: false,
782 };
783 let two_kinds_of_msg_format_err = "cannot specify two kinds of `message-format` arguments";
784 for fmt in self._values_of("message-format") {
785 for fmt in fmt.split(',') {
786 let fmt = fmt.to_ascii_lowercase();
787 match fmt.as_str() {
788 "json" => {
789 if message_format.is_some() {
790 bail!(two_kinds_of_msg_format_err);
791 }
792 message_format = Some(default_json);
793 }
794 "human" => {
795 if message_format.is_some() {
796 bail!(two_kinds_of_msg_format_err);
797 }
798 message_format = Some(MessageFormat::Human);
799 }
800 "short" => {
801 if message_format.is_some() {
802 bail!(two_kinds_of_msg_format_err);
803 }
804 message_format = Some(MessageFormat::Short);
805 }
806 "json-render-diagnostics" => {
807 if message_format.is_none() {
808 message_format = Some(default_json);
809 }
810 match &mut message_format {
811 Some(MessageFormat::Json {
812 render_diagnostics, ..
813 }) => *render_diagnostics = true,
814 _ => bail!(two_kinds_of_msg_format_err),
815 }
816 }
817 "json-diagnostic-short" => {
818 if message_format.is_none() {
819 message_format = Some(default_json);
820 }
821 match &mut message_format {
822 Some(MessageFormat::Json { short, .. }) => *short = true,
823 _ => bail!(two_kinds_of_msg_format_err),
824 }
825 }
826 "json-diagnostic-rendered-ansi" => {
827 if message_format.is_none() {
828 message_format = Some(default_json);
829 }
830 match &mut message_format {
831 Some(MessageFormat::Json { ansi, .. }) => *ansi = true,
832 _ => bail!(two_kinds_of_msg_format_err),
833 }
834 }
835 s => bail!("invalid message format specifier: `{}`", s),
836 }
837 }
838 }
839
840 let mut build_config = BuildConfig::new(
841 gctx,
842 self.jobs()?,
843 self.keep_going(),
844 &self.targets()?,
845 intent,
846 )?;
847 build_config.message_format = message_format.unwrap_or(MessageFormat::Human);
848 build_config.requested_profile = self.get_profile_name("dev", profile_checking)?;
849 build_config.unit_graph = self.flag("unit-graph");
850 build_config.future_incompat_report = self.flag("future-incompat-report");
851 build_config.compile_time_deps_only = self.flag("compile-time-deps");
852 build_config.timing_report = self.flag("timings");
853
854 if build_config.unit_graph {
855 gctx.cli_unstable()
856 .fail_if_stable_opt("--unit-graph", 8002)?;
857 }
858 if build_config.compile_time_deps_only {
859 gctx.cli_unstable()
860 .fail_if_stable_opt("--compile-time-deps", 14434)?;
861 }
862
863 let opts = CompileOptions {
864 build_config,
865 cli_features: self.cli_features()?,
866 spec,
867 filter: CompileFilter::from_raw_arguments(
868 self.flag("lib"),
869 self._values_of("bin"),
870 self.flag("bins"),
871 self._values_of("test"),
872 self.flag("tests"),
873 self._values_of("example"),
874 self.flag("examples"),
875 self._values_of("bench"),
876 self.flag("benches"),
877 self.flag("all-targets"),
878 ),
879 target_rustdoc_args: None,
880 target_rustc_args: None,
881 target_rustc_crate_types: None,
882 rustdoc_document_private_items: false,
883 honor_rust_version: self.honor_rust_version(),
884 };
885
886 if let Some(ws) = workspace {
887 self.check_optional_opts(ws, &opts)?;
888 } else if self.is_present_with_zero_values("package") {
889 anyhow::bail!(
892 "\"--package <SPEC>\" requires a SPEC format value, \
893 which can be any package ID specifier in the dependency graph.\n\
894 Run `cargo help pkgid` for more information about SPEC format."
895 )
896 }
897
898 Ok(opts)
899 }
900
901 fn cli_features(&self) -> CargoResult<CliFeatures> {
902 CliFeatures::from_command_line(
903 &self._values_of("features"),
904 self.flag("all-features"),
905 !self.flag("no-default-features"),
906 )
907 }
908
909 fn compile_options_for_single_package(
910 &self,
911 gctx: &GlobalContext,
912 intent: UserIntent,
913 workspace: Option<&Workspace<'_>>,
914 profile_checking: ProfileChecking,
915 ) -> CargoResult<CompileOptions> {
916 let mut compile_opts = self.compile_options(gctx, intent, workspace, profile_checking)?;
917 let spec = self._values_of("package");
918 if spec.iter().any(restricted_names::is_glob_pattern) {
919 anyhow::bail!("Glob patterns on package selection are not supported.")
920 }
921 compile_opts.spec = Packages::Packages(spec);
922 Ok(compile_opts)
923 }
924
925 fn new_options(&self, gctx: &GlobalContext) -> CargoResult<NewOptions> {
926 let vcs = self._value_of("vcs").map(|vcs| match vcs {
927 "git" => VersionControl::Git,
928 "hg" => VersionControl::Hg,
929 "pijul" => VersionControl::Pijul,
930 "fossil" => VersionControl::Fossil,
931 "none" => VersionControl::NoVcs,
932 vcs => panic!("Impossible vcs: {:?}", vcs),
933 });
934 NewOptions::new(
935 vcs,
936 self.flag("bin"),
937 self.flag("lib"),
938 self.value_of_path("path", gctx).unwrap(),
939 self._value_of("name").map(|s| s.to_string()),
940 self._value_of("edition").map(|s| s.to_string()),
941 self.registry(gctx)?,
942 )
943 }
944
945 fn registry_or_index(&self, gctx: &GlobalContext) -> CargoResult<Option<RegistryOrIndex>> {
946 let registry = self._value_of("registry");
947 let index = self._value_of("index");
948 let result = match (registry, index) {
949 (None, None) => gctx.default_registry()?.map(RegistryOrIndex::Registry),
950 (None, Some(i)) => Some(RegistryOrIndex::Index(i.into_url()?)),
951 (Some(r), None) => {
952 RegistryName::new(r)?;
953 Some(RegistryOrIndex::Registry(r.to_string()))
954 }
955 (Some(_), Some(_)) => {
956 unreachable!("both `--index` and `--registry` should not be set at the same time")
958 }
959 };
960 Ok(result)
961 }
962
963 fn registry(&self, gctx: &GlobalContext) -> CargoResult<Option<String>> {
964 match self._value_of("registry").map(|s| s.to_string()) {
965 None => gctx.default_registry(),
966 Some(registry) => {
967 RegistryName::new(®istry)?;
968 Ok(Some(registry))
969 }
970 }
971 }
972
973 fn check_optional_opts(
974 &self,
975 workspace: &Workspace<'_>,
976 compile_opts: &CompileOptions,
977 ) -> CargoResult<()> {
978 if self.is_present_with_zero_values("package") {
979 print_available_packages(workspace)?
980 }
981
982 if self.is_present_with_zero_values("example") {
983 print_available_examples(workspace, compile_opts)?;
984 }
985
986 if self.is_present_with_zero_values("bin") {
987 print_available_binaries(workspace, compile_opts)?;
988 }
989
990 if self.is_present_with_zero_values("bench") {
991 print_available_benches(workspace, compile_opts)?;
992 }
993
994 if self.is_present_with_zero_values("test") {
995 print_available_tests(workspace, compile_opts)?;
996 }
997
998 Ok(())
999 }
1000
1001 fn is_present_with_zero_values(&self, name: &str) -> bool {
1002 self._contains(name) && self._value_of(name).is_none()
1003 }
1004
1005 fn flag(&self, name: &str) -> bool;
1006
1007 fn maybe_flag(&self, name: &str) -> bool;
1008
1009 fn _value_of(&self, name: &str) -> Option<&str>;
1010
1011 fn _values_of(&self, name: &str) -> Vec<String>;
1012
1013 fn _value_of_os(&self, name: &str) -> Option<&OsStr>;
1014
1015 fn _values_of_os(&self, name: &str) -> Vec<OsString>;
1016
1017 fn _count(&self, name: &str) -> u32;
1018
1019 fn _contains(&self, name: &str) -> bool;
1020}
1021
1022impl<'a> ArgMatchesExt for ArgMatches {
1023 fn flag(&self, name: &str) -> bool {
1024 ignore_unknown(self.try_get_one::<bool>(name))
1025 .copied()
1026 .unwrap_or(false)
1027 }
1028
1029 fn maybe_flag(&self, name: &str) -> bool {
1033 self.try_get_one::<bool>(name)
1034 .ok()
1035 .flatten()
1036 .copied()
1037 .unwrap_or_default()
1038 }
1039
1040 fn _value_of(&self, name: &str) -> Option<&str> {
1041 ignore_unknown(self.try_get_one::<String>(name)).map(String::as_str)
1042 }
1043
1044 fn _value_of_os(&self, name: &str) -> Option<&OsStr> {
1045 ignore_unknown(self.try_get_one::<OsString>(name)).map(OsString::as_os_str)
1046 }
1047
1048 fn _values_of(&self, name: &str) -> Vec<String> {
1049 ignore_unknown(self.try_get_many::<String>(name))
1050 .unwrap_or_default()
1051 .cloned()
1052 .collect()
1053 }
1054
1055 fn _values_of_os(&self, name: &str) -> Vec<OsString> {
1056 ignore_unknown(self.try_get_many::<OsString>(name))
1057 .unwrap_or_default()
1058 .cloned()
1059 .collect()
1060 }
1061
1062 fn _count(&self, name: &str) -> u32 {
1063 *ignore_unknown(self.try_get_one::<u8>(name)).expect("defaulted by clap") as u32
1064 }
1065
1066 fn _contains(&self, name: &str) -> bool {
1067 ignore_unknown(self.try_contains_id(name))
1068 }
1069}
1070
1071pub fn values(args: &ArgMatches, name: &str) -> Vec<String> {
1072 args._values_of(name)
1073}
1074
1075pub fn values_os(args: &ArgMatches, name: &str) -> Vec<OsString> {
1076 args._values_of_os(name)
1077}
1078
1079pub fn root_manifest(manifest_path: Option<&Path>, gctx: &GlobalContext) -> CargoResult<PathBuf> {
1080 if let Some(manifest_path) = manifest_path {
1081 let path = gctx.cwd().join(manifest_path);
1082 let path = paths::normalize_path(&path);
1085 if !path.ends_with("Cargo.toml") && !crate::util::toml::is_embedded(&path) {
1086 anyhow::bail!(
1087 "the manifest-path must be a path to a Cargo.toml file: `{}`",
1088 path.display()
1089 )
1090 }
1091 if !path.exists() {
1092 anyhow::bail!("manifest path `{}` does not exist", manifest_path.display())
1093 }
1094 if path.is_dir() {
1095 anyhow::bail!(
1096 "manifest path `{}` is a directory but expected a file",
1097 manifest_path.display()
1098 )
1099 }
1100 if crate::util::toml::is_embedded(&path) && !gctx.cli_unstable().script {
1101 anyhow::bail!("embedded manifest `{}` requires `-Zscript`", path.display())
1102 }
1103 Ok(path)
1104 } else {
1105 find_root_manifest_for_wd(gctx.cwd())
1106 }
1107}
1108
1109pub fn lockfile_path(
1110 lockfile_path: Option<&Path>,
1111 gctx: &GlobalContext,
1112) -> CargoResult<Option<PathBuf>> {
1113 let Some(lockfile_path) = lockfile_path else {
1114 return Ok(None);
1115 };
1116
1117 gctx.cli_unstable()
1118 .fail_if_stable_opt("--lockfile-path", 14421)?;
1119
1120 let path = gctx.cwd().join(lockfile_path);
1121
1122 if !path.ends_with(LOCKFILE_NAME) {
1123 bail!(
1124 "the lockfile-path must be a path to a {LOCKFILE_NAME} file (please rename your lock file to {LOCKFILE_NAME})"
1125 )
1126 }
1127 if path.is_dir() {
1128 bail!(
1129 "lockfile path `{}` is a directory but expected a file",
1130 lockfile_path.display()
1131 )
1132 }
1133
1134 gctx.shell().warn(
1135 "the `--lockfile-path` flag is deprecated and will be removed in a future release, \
1136 use `resolver.lockfile-path` config instead",
1137 )?;
1138
1139 return Ok(Some(path));
1140}
1141
1142pub fn get_registry_candidates() -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1143 let gctx = new_gctx_for_completions()?;
1144
1145 if let Ok(Some(registries)) =
1146 gctx.get::<Option<HashMap<String, HashMap<String, String>>>>("registries")
1147 {
1148 Ok(registries
1149 .keys()
1150 .map(|name| clap_complete::CompletionCandidate::new(name.to_owned()))
1151 .collect())
1152 } else {
1153 Ok(vec![])
1154 }
1155}
1156
1157fn get_profile_candidates() -> Vec<clap_complete::CompletionCandidate> {
1158 match get_workspace_profile_candidates() {
1159 Ok(candidates) if !candidates.is_empty() => candidates,
1160 _ => default_profile_candidates(),
1162 }
1163}
1164
1165fn get_workspace_profile_candidates() -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1166 let gctx = new_gctx_for_completions()?;
1167 let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?;
1168 let profiles = Profiles::new(&ws, "dev".into())?;
1169
1170 let mut candidates = Vec::new();
1171 for name in profiles.profile_names() {
1172 let Ok(profile_instance) = Profiles::new(&ws, name) else {
1173 continue;
1174 };
1175 let base_profile = profile_instance.base_profile();
1176
1177 let mut description = String::from(if base_profile.opt_level.as_str() == "0" {
1178 "unoptimized"
1179 } else {
1180 "optimized"
1181 });
1182
1183 if base_profile.debuginfo.is_turned_on() {
1184 description.push_str(" + debuginfo");
1185 }
1186
1187 candidates
1188 .push(clap_complete::CompletionCandidate::new(&name).help(Some(description.into())));
1189 }
1190
1191 Ok(candidates)
1192}
1193
1194fn default_profile_candidates() -> Vec<clap_complete::CompletionCandidate> {
1195 vec![
1196 clap_complete::CompletionCandidate::new("dev").help(Some("unoptimized + debuginfo".into())),
1197 clap_complete::CompletionCandidate::new("release").help(Some("optimized".into())),
1198 clap_complete::CompletionCandidate::new("test")
1199 .help(Some("unoptimized + debuginfo".into())),
1200 clap_complete::CompletionCandidate::new("bench").help(Some("optimized".into())),
1201 ]
1202}
1203
1204fn get_feature_candidates() -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1205 let gctx = new_gctx_for_completions()?;
1206
1207 let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?;
1208 let mut feature_candidates = Vec::new();
1209
1210 for package in ws.members() {
1212 let package_name = package.name();
1213
1214 for feature_name in package.summary().features().keys() {
1216 let order = if ws.current_opt().map(|p| p.name()) == Some(package_name) {
1217 0
1218 } else {
1219 1
1220 };
1221 feature_candidates.push(
1222 clap_complete::CompletionCandidate::new(feature_name)
1223 .display_order(Some(order))
1224 .help(Some(format!("from {}", package_name).into())),
1225 );
1226 }
1227 }
1228
1229 Ok(feature_candidates)
1230}
1231
1232fn get_crate_candidates(kind: TargetKind) -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1233 let gctx = new_gctx_for_completions()?;
1234
1235 let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?;
1236
1237 let targets = ws
1238 .members()
1239 .flat_map(|pkg| pkg.targets().into_iter().cloned().map(|t| (pkg.name(), t)))
1240 .filter(|(_, target)| *target.kind() == kind)
1241 .map(|(pkg_name, target)| {
1242 let order = if ws.current_opt().map(|p| p.name()) == Some(pkg_name) {
1243 0
1244 } else {
1245 1
1246 };
1247 clap_complete::CompletionCandidate::new(target.name())
1248 .display_order(Some(order))
1249 .help(Some(format!("from {}", pkg_name).into()))
1250 })
1251 .collect::<Vec<_>>();
1252
1253 Ok(targets)
1254}
1255
1256fn get_target_triples() -> Vec<clap_complete::CompletionCandidate> {
1257 let mut candidates = Vec::new();
1258
1259 if let Ok(targets) = get_target_triples_from_rustup() {
1260 candidates = targets;
1261 }
1262
1263 if candidates.is_empty() {
1264 if let Ok(targets) = get_target_triples_from_rustc() {
1265 candidates = targets;
1266 }
1267 }
1268
1269 candidates.insert(
1271 0,
1272 clap_complete::CompletionCandidate::new("host-tuple").help(Some(
1273 concat!("alias for: ", env!("RUST_HOST_TARGET")).into(),
1274 )),
1275 );
1276
1277 candidates
1278}
1279
1280pub fn get_target_triples_with_all() -> Vec<clap_complete::CompletionCandidate> {
1281 let mut candidates = vec![
1282 clap_complete::CompletionCandidate::new("all").help(Some("Include all targets".into())),
1283 ];
1284 candidates.extend(get_target_triples());
1285 candidates
1286}
1287
1288fn get_target_triples_from_rustup() -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1289 let output = std::process::Command::new("rustup")
1290 .arg("target")
1291 .arg("list")
1292 .output()?;
1293
1294 if !output.status.success() {
1295 return Ok(vec![]);
1296 }
1297
1298 let stdout = String::from_utf8(output.stdout)?;
1299
1300 Ok(stdout
1301 .lines()
1302 .map(|line| {
1303 let target = line.split_once(' ');
1304 match target {
1305 None => clap_complete::CompletionCandidate::new(line.to_owned()).hide(true),
1306 Some((target, _installed)) => clap_complete::CompletionCandidate::new(target),
1307 }
1308 })
1309 .collect())
1310}
1311
1312fn get_target_triples_from_rustc() -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1313 let gctx = new_gctx_for_completions()?;
1314
1315 let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx);
1316
1317 let rustc = gctx.load_global_rustc(ws.as_ref().ok())?;
1318
1319 let (stdout, _stderr) =
1320 rustc.cached_output(rustc.process().arg("--print").arg("target-list"), 0)?;
1321
1322 Ok(stdout
1323 .lines()
1324 .map(|line| clap_complete::CompletionCandidate::new(line.to_owned()))
1325 .collect())
1326}
1327
1328pub fn get_ws_member_candidates() -> Vec<clap_complete::CompletionCandidate> {
1329 get_ws_member_packages()
1330 .unwrap_or_default()
1331 .into_iter()
1332 .map(|pkg| {
1333 clap_complete::CompletionCandidate::new(pkg.name().as_str()).help(
1334 pkg.manifest()
1335 .metadata()
1336 .description
1337 .to_owned()
1338 .map(From::from),
1339 )
1340 })
1341 .collect::<Vec<_>>()
1342}
1343
1344fn get_ws_member_packages() -> CargoResult<Vec<Package>> {
1345 let gctx = new_gctx_for_completions()?;
1346 let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?;
1347 let packages = ws.members().map(Clone::clone).collect::<Vec<_>>();
1348 Ok(packages)
1349}
1350
1351pub fn get_pkg_id_spec_candidates() -> Vec<clap_complete::CompletionCandidate> {
1352 let mut candidates = vec![];
1353
1354 let package_map = HashMap::<&str, Vec<Package>>::new();
1355 let package_map =
1356 get_packages()
1357 .unwrap_or_default()
1358 .into_iter()
1359 .fold(package_map, |mut map, package| {
1360 map.entry(package.name().as_str())
1361 .or_insert_with(Vec::new)
1362 .push(package);
1363 map
1364 });
1365
1366 let unique_name_candidates = package_map
1367 .iter()
1368 .filter(|(_name, packages)| packages.len() == 1)
1369 .map(|(name, packages)| {
1370 clap_complete::CompletionCandidate::new(name.to_string()).help(
1371 packages[0]
1372 .manifest()
1373 .metadata()
1374 .description
1375 .to_owned()
1376 .map(From::from),
1377 )
1378 })
1379 .collect::<Vec<_>>();
1380
1381 let duplicate_name_pairs = package_map
1382 .iter()
1383 .filter(|(_name, packages)| packages.len() > 1)
1384 .collect::<Vec<_>>();
1385
1386 let mut duplicate_name_candidates = vec![];
1387 for (name, packages) in duplicate_name_pairs {
1388 let mut version_count: HashMap<&Version, usize> = HashMap::new();
1389
1390 for package in packages {
1391 *version_count.entry(package.version()).or_insert(0) += 1;
1392 }
1393
1394 for package in packages {
1395 if let Some(&count) = version_count.get(package.version()) {
1396 if count == 1 {
1397 duplicate_name_candidates.push(
1398 clap_complete::CompletionCandidate::new(format!(
1399 "{}@{}",
1400 name,
1401 package.version()
1402 ))
1403 .help(
1404 package
1405 .manifest()
1406 .metadata()
1407 .description
1408 .to_owned()
1409 .map(From::from),
1410 ),
1411 );
1412 } else {
1413 duplicate_name_candidates.push(
1414 clap_complete::CompletionCandidate::new(format!(
1415 "{}",
1416 package.package_id().to_spec()
1417 ))
1418 .help(
1419 package
1420 .manifest()
1421 .metadata()
1422 .description
1423 .to_owned()
1424 .map(From::from),
1425 ),
1426 )
1427 }
1428 }
1429 }
1430 }
1431
1432 candidates.extend(unique_name_candidates);
1433 candidates.extend(duplicate_name_candidates);
1434
1435 candidates
1436}
1437
1438pub fn get_pkg_name_candidates() -> Vec<clap_complete::CompletionCandidate> {
1439 let packages: BTreeMap<_, _> = get_packages()
1440 .unwrap_or_default()
1441 .into_iter()
1442 .map(|package| {
1443 (
1444 package.name(),
1445 package.manifest().metadata().description.clone(),
1446 )
1447 })
1448 .collect();
1449
1450 packages
1451 .into_iter()
1452 .map(|(name, description)| {
1453 clap_complete::CompletionCandidate::new(name.as_str()).help(description.map(From::from))
1454 })
1455 .collect()
1456}
1457
1458fn get_packages() -> CargoResult<Vec<Package>> {
1459 let gctx = new_gctx_for_completions()?;
1460
1461 let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?;
1462
1463 let requested_kinds = CompileKind::from_requested_targets(ws.gctx(), &[])?;
1464 let mut target_data = RustcTargetData::new(&ws, &requested_kinds)?;
1465 let cli_features = CliFeatures::new_all(true);
1467 let has_dev_units = HasDevUnits::Yes;
1468 let force_all_targets = ForceAllTargets::No;
1469 let dry_run = true;
1470
1471 let ws_resolve = ops::resolve_ws_with_opts(
1472 &ws,
1473 &mut target_data,
1474 &requested_kinds,
1475 &cli_features,
1476 &[],
1477 has_dev_units,
1478 force_all_targets,
1479 dry_run,
1480 )?;
1481
1482 let packages = ws_resolve
1483 .pkg_set
1484 .packages()
1485 .map(Clone::clone)
1486 .collect::<Vec<_>>();
1487
1488 Ok(packages)
1489}
1490
1491pub fn get_direct_dependencies_pkg_name_candidates() -> Vec<clap_complete::CompletionCandidate> {
1492 let (current_package_deps, all_package_deps) = match get_dependencies_from_metadata() {
1493 Ok(v) => v,
1494 Err(_) => return Vec::new(),
1495 };
1496
1497 let current_package_deps_package_names = current_package_deps
1498 .into_iter()
1499 .map(|dep| dep.package_name().to_string())
1500 .sorted();
1501 let all_package_deps_package_names = all_package_deps
1502 .into_iter()
1503 .map(|dep| dep.package_name().to_string())
1504 .sorted();
1505
1506 let mut package_names_set = IndexSet::new();
1507 package_names_set.extend(current_package_deps_package_names);
1508 package_names_set.extend(all_package_deps_package_names);
1509
1510 package_names_set
1511 .into_iter()
1512 .map(|name| name.into())
1513 .collect_vec()
1514}
1515
1516fn get_dependencies_from_metadata() -> CargoResult<(Vec<Dependency>, Vec<Dependency>)> {
1517 let cwd = std::env::current_dir()?;
1518 let gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?);
1519 let ws = Workspace::new(&find_root_manifest_for_wd(&cwd)?, &gctx)?;
1520 let current_package = ws.current().ok();
1521
1522 let current_package_dependencies = ws
1523 .current()
1524 .map(|current| current.dependencies())
1525 .unwrap_or_default()
1526 .to_vec();
1527 let all_other_packages_dependencies = ws
1528 .members()
1529 .filter(|&member| Some(member) != current_package)
1530 .flat_map(|pkg| pkg.dependencies().into_iter().cloned())
1531 .collect::<HashSet<_>>()
1532 .into_iter()
1533 .collect::<Vec<_>>();
1534
1535 Ok((
1536 current_package_dependencies,
1537 all_other_packages_dependencies,
1538 ))
1539}
1540
1541pub fn new_gctx_for_completions() -> CargoResult<GlobalContext> {
1542 let cwd = std::env::current_dir()?;
1543 let mut gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?);
1544
1545 let verbose = 0;
1546 let quiet = true;
1547 let color = None;
1548 let frozen = false;
1549 let locked = true;
1550 let offline = false;
1551 let target_dir = None;
1552 let unstable_flags = &[];
1553 let cli_config = &[];
1554
1555 gctx.configure(
1556 verbose,
1557 quiet,
1558 color,
1559 frozen,
1560 locked,
1561 offline,
1562 &target_dir,
1563 unstable_flags,
1564 cli_config,
1565 )?;
1566
1567 Ok(gctx)
1568}
1569
1570#[track_caller]
1571pub fn ignore_unknown<T: Default>(r: Result<T, clap::parser::MatchesError>) -> T {
1572 match r {
1573 Ok(t) => t,
1574 Err(clap::parser::MatchesError::UnknownArgument { .. }) => Default::default(),
1575 Err(e) => {
1576 panic!("Mismatch between definition and access: {}", e);
1577 }
1578 }
1579}
1580
1581#[derive(PartialEq, Eq, PartialOrd, Ord)]
1582pub enum CommandInfo {
1583 BuiltIn { about: Option<String> },
1584 External { path: PathBuf },
1585 Alias { target: StringOrVec },
1586}