Skip to main content

cargo/util/
command_prelude.rs

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