Skip to main content

cargo/util/
command_prelude.rs

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