Skip to main content

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