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