#
cargo/util/
command_prelude.rs

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