From 5d06e69710646a5de58c7aac0f0a1d9dfb80d2c2 Mon Sep 17 00:00:00 2001 From: shannmu Date: Wed, 14 Aug 2024 17:32:11 +0800 Subject: [PATCH] feat: Add custom completer for completing bin names --- src/cargo/util/command_prelude.rs | 79 +++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 15 deletions(-) diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index 9b8ff13e7fdc..a495fdd70cc6 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -13,7 +13,7 @@ use crate::util::{ print_available_packages, print_available_tests, }; use crate::CargoResult; -use anyhow::bail; +use anyhow::{anyhow, bail}; use cargo_util::paths; use cargo_util_schemas::manifest::ProfileName; use cargo_util_schemas::manifest::RegistryName; @@ -173,7 +173,11 @@ pub trait CommandExt: Sized { ) -> Self { self._arg(flag("lib", lib).help_heading(heading::TARGET_SELECTION)) ._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION)) - ._arg(optional_multi_opt("bin", "NAME", bin).help_heading(heading::TARGET_SELECTION)) + ._arg( + optional_multi_opt("bin", "NAME", bin) + .help_heading(heading::TARGET_SELECTION) + .add(clap_complete::ArgValueCandidates::new(get_bin_candidates)), + ) ._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION)) ._arg( optional_multi_opt("example", "NAME", example) @@ -188,21 +192,27 @@ pub trait CommandExt: Sized { example: &'static str, examples: &'static str, ) -> Self { - self._arg(optional_multi_opt("bin", "NAME", bin).help_heading(heading::TARGET_SELECTION)) - ._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION)) - ._arg( - optional_multi_opt("example", "NAME", example) - .help_heading(heading::TARGET_SELECTION), - ) - ._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION)) + self._arg( + optional_multi_opt("bin", "NAME", bin) + .help_heading(heading::TARGET_SELECTION) + .add(clap_complete::ArgValueCandidates::new(get_bin_candidates)), + ) + ._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION)) + ._arg( + optional_multi_opt("example", "NAME", example).help_heading(heading::TARGET_SELECTION), + ) + ._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION)) } fn arg_targets_bin_example(self, bin: &'static str, example: &'static str) -> Self { - self._arg(optional_multi_opt("bin", "NAME", bin).help_heading(heading::TARGET_SELECTION)) - ._arg( - optional_multi_opt("example", "NAME", example) - .help_heading(heading::TARGET_SELECTION), - ) + self._arg( + optional_multi_opt("bin", "NAME", bin) + .help_heading(heading::TARGET_SELECTION) + .add(clap_complete::ArgValueCandidates::new(get_bin_candidates)), + ) + ._arg( + optional_multi_opt("example", "NAME", example).help_heading(heading::TARGET_SELECTION), + ) } fn arg_features(self) -> Self { @@ -333,7 +343,10 @@ pub trait CommandExt: Sized { .value_name("VCS") .value_parser(["git", "hg", "pijul", "fossil", "none"]), ) - ._arg(flag("bin", "Use a binary (application) template [default]")) + ._arg( + flag("bin", "Use a binary (application) template [default]") + .add(clap_complete::ArgValueCandidates::new(get_bin_candidates)), + ) ._arg(flag("lib", "Use a library template")) ._arg( opt("edition", "Edition to set for the crate generated") @@ -1027,6 +1040,42 @@ pub fn lockfile_path( return Ok(Some(path)); } +fn get_bin_candidates() -> Vec { + get_bins_from_manifest() + .map(|bins| { + bins.into_iter() + .map(|bin| clap_complete::CompletionCandidate::new(bin)) + .collect() + }) + .unwrap_or(vec![]) +} + +fn get_bins_from_manifest() -> Option> { + let manifest = get_manifest_with_table().ok()?; + let mut bins = vec![]; + + let bins_array = manifest.get("bin")?.as_array()?; + + for bin in bins_array { + let name = bin.as_table()?.get("name")?.as_str()?; + bins.push(name.to_owned()); + } + + Some(bins) +} + +fn get_manifest_with_table() -> CargoResult { + let menifest = env!("CARGO_MANIFEST_DIR"); + let menifest = Path::new(menifest).join("Cargo.toml"); + + let value = toml::from_str::(&std::fs::read_to_string(menifest)?)?; + + match value { + toml::Value::Table(table) => Ok(table), + _ => Err(anyhow!("Error parsing Cargo.toml")), + } +} + #[track_caller] pub fn ignore_unknown(r: Result) -> T { match r {