#
cargo/util/
command_prelude.rs

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