cargo/util/
command_prelude.rs

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