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