Skip to content

Commit 5d06e69

Browse files
committed
feat: Add custom completer for completing bin names
1 parent e7ca9be commit 5d06e69

File tree

1 file changed

+64
-15
lines changed

1 file changed

+64
-15
lines changed

src/cargo/util/command_prelude.rs

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::util::{
1313
print_available_packages, print_available_tests,
1414
};
1515
use crate::CargoResult;
16-
use anyhow::bail;
16+
use anyhow::{anyhow, bail};
1717
use cargo_util::paths;
1818
use cargo_util_schemas::manifest::ProfileName;
1919
use cargo_util_schemas::manifest::RegistryName;
@@ -173,7 +173,11 @@ pub trait CommandExt: Sized {
173173
) -> Self {
174174
self._arg(flag("lib", lib).help_heading(heading::TARGET_SELECTION))
175175
._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION))
176-
._arg(optional_multi_opt("bin", "NAME", bin).help_heading(heading::TARGET_SELECTION))
176+
._arg(
177+
optional_multi_opt("bin", "NAME", bin)
178+
.help_heading(heading::TARGET_SELECTION)
179+
.add(clap_complete::ArgValueCandidates::new(get_bin_candidates)),
180+
)
177181
._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION))
178182
._arg(
179183
optional_multi_opt("example", "NAME", example)
@@ -188,21 +192,27 @@ pub trait CommandExt: Sized {
188192
example: &'static str,
189193
examples: &'static str,
190194
) -> Self {
191-
self._arg(optional_multi_opt("bin", "NAME", bin).help_heading(heading::TARGET_SELECTION))
192-
._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION))
193-
._arg(
194-
optional_multi_opt("example", "NAME", example)
195-
.help_heading(heading::TARGET_SELECTION),
196-
)
197-
._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION))
195+
self._arg(
196+
optional_multi_opt("bin", "NAME", bin)
197+
.help_heading(heading::TARGET_SELECTION)
198+
.add(clap_complete::ArgValueCandidates::new(get_bin_candidates)),
199+
)
200+
._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION))
201+
._arg(
202+
optional_multi_opt("example", "NAME", example).help_heading(heading::TARGET_SELECTION),
203+
)
204+
._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION))
198205
}
199206

200207
fn arg_targets_bin_example(self, bin: &'static str, example: &'static str) -> Self {
201-
self._arg(optional_multi_opt("bin", "NAME", bin).help_heading(heading::TARGET_SELECTION))
202-
._arg(
203-
optional_multi_opt("example", "NAME", example)
204-
.help_heading(heading::TARGET_SELECTION),
205-
)
208+
self._arg(
209+
optional_multi_opt("bin", "NAME", bin)
210+
.help_heading(heading::TARGET_SELECTION)
211+
.add(clap_complete::ArgValueCandidates::new(get_bin_candidates)),
212+
)
213+
._arg(
214+
optional_multi_opt("example", "NAME", example).help_heading(heading::TARGET_SELECTION),
215+
)
206216
}
207217

208218
fn arg_features(self) -> Self {
@@ -333,7 +343,10 @@ pub trait CommandExt: Sized {
333343
.value_name("VCS")
334344
.value_parser(["git", "hg", "pijul", "fossil", "none"]),
335345
)
336-
._arg(flag("bin", "Use a binary (application) template [default]"))
346+
._arg(
347+
flag("bin", "Use a binary (application) template [default]")
348+
.add(clap_complete::ArgValueCandidates::new(get_bin_candidates)),
349+
)
337350
._arg(flag("lib", "Use a library template"))
338351
._arg(
339352
opt("edition", "Edition to set for the crate generated")
@@ -1027,6 +1040,42 @@ pub fn lockfile_path(
10271040
return Ok(Some(path));
10281041
}
10291042

1043+
fn get_bin_candidates() -> Vec<clap_complete::CompletionCandidate> {
1044+
get_bins_from_manifest()
1045+
.map(|bins| {
1046+
bins.into_iter()
1047+
.map(|bin| clap_complete::CompletionCandidate::new(bin))
1048+
.collect()
1049+
})
1050+
.unwrap_or(vec![])
1051+
}
1052+
1053+
fn get_bins_from_manifest() -> Option<Vec<String>> {
1054+
let manifest = get_manifest_with_table().ok()?;
1055+
let mut bins = vec![];
1056+
1057+
let bins_array = manifest.get("bin")?.as_array()?;
1058+
1059+
for bin in bins_array {
1060+
let name = bin.as_table()?.get("name")?.as_str()?;
1061+
bins.push(name.to_owned());
1062+
}
1063+
1064+
Some(bins)
1065+
}
1066+
1067+
fn get_manifest_with_table() -> CargoResult<toml::Table> {
1068+
let menifest = env!("CARGO_MANIFEST_DIR");
1069+
let menifest = Path::new(menifest).join("Cargo.toml");
1070+
1071+
let value = toml::from_str::<toml::Value>(&std::fs::read_to_string(menifest)?)?;
1072+
1073+
match value {
1074+
toml::Value::Table(table) => Ok(table),
1075+
_ => Err(anyhow!("Error parsing Cargo.toml")),
1076+
}
1077+
}
1078+
10301079
#[track_caller]
10311080
pub fn ignore_unknown<T: Default>(r: Result<T, clap::parser::MatchesError>) -> T {
10321081
match r {

0 commit comments

Comments
 (0)