#
cargo/util/
command_prelude.rs

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