cargo/util/
command_prelude.rs

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