Skip to main content

cargo/util/
command_prelude.rs

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