mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 14:52:33 +00:00
Merge #2855
2855: fix(gen)!: Simplify `Generator` trait r=pksunkara a=epage Co-authored-by: Ed Page <eopage@gmail.com>
This commit is contained in:
commit
36e172672c
8 changed files with 261 additions and 269 deletions
|
@ -4,7 +4,7 @@ mod shells;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
use clap::{App, Arg};
|
use clap::App;
|
||||||
pub use shells::*;
|
pub use shells::*;
|
||||||
|
|
||||||
/// Generator trait which can be used to write generators
|
/// Generator trait which can be used to write generators
|
||||||
|
@ -53,255 +53,4 @@ pub trait Generator {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn generate(&self, app: &App, buf: &mut dyn Write);
|
fn generate(&self, app: &App, buf: &mut dyn Write);
|
||||||
|
|
||||||
/// Gets all subcommands including child subcommands in the form of `("name", "bin_name")`.
|
|
||||||
///
|
|
||||||
/// Subcommand `rustup toolchain install` would be converted to
|
|
||||||
/// `("install", "rustup toolchain install")`.
|
|
||||||
fn all_subcommands(&self, app: &App) -> Vec<(String, String)> {
|
|
||||||
let mut subcmds: Vec<_> = self.subcommands(app);
|
|
||||||
|
|
||||||
for sc_v in app.get_subcommands().map(|s| self.all_subcommands(s)) {
|
|
||||||
subcmds.extend(sc_v);
|
|
||||||
}
|
|
||||||
|
|
||||||
subcmds
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds the subcommand [`clap::App`] from the given [`clap::App`] with the given path.
|
|
||||||
///
|
|
||||||
/// **NOTE:** `path` should not contain the root `bin_name`.
|
|
||||||
fn find_subcommand_with_path<'help, 'app>(
|
|
||||||
&self,
|
|
||||||
p: &'app App<'help>,
|
|
||||||
path: Vec<&str>,
|
|
||||||
) -> &'app App<'help> {
|
|
||||||
let mut app = p;
|
|
||||||
|
|
||||||
for sc in path {
|
|
||||||
app = app.find_subcommand(sc).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
app
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets subcommands of [`clap::App`] in the form of `("name", "bin_name")`.
|
|
||||||
///
|
|
||||||
/// Subcommand `rustup toolchain install` would be converted to
|
|
||||||
/// `("install", "rustup toolchain install")`.
|
|
||||||
fn subcommands(&self, p: &App) -> Vec<(String, String)> {
|
|
||||||
debug!("subcommands: name={}", p.get_name());
|
|
||||||
debug!("subcommands: Has subcommands...{:?}", p.has_subcommands());
|
|
||||||
|
|
||||||
let mut subcmds = vec![];
|
|
||||||
|
|
||||||
if !p.has_subcommands() {
|
|
||||||
return subcmds;
|
|
||||||
}
|
|
||||||
|
|
||||||
for sc in p.get_subcommands() {
|
|
||||||
let sc_bin_name = sc.get_bin_name().unwrap();
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
"subcommands:iter: name={}, bin_name={}",
|
|
||||||
sc.get_name(),
|
|
||||||
sc_bin_name
|
|
||||||
);
|
|
||||||
|
|
||||||
subcmds.push((sc.get_name().to_string(), sc_bin_name.to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
subcmds
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets all the short options, their visible aliases and flags of a [`clap::App`].
|
|
||||||
/// Includes `h` and `V` depending on the [`clap::AppSettings`].
|
|
||||||
fn shorts_and_visible_aliases(&self, p: &App) -> Vec<char> {
|
|
||||||
debug!("shorts: name={}", p.get_name());
|
|
||||||
|
|
||||||
p.get_arguments()
|
|
||||||
.filter_map(|a| {
|
|
||||||
if a.get_index().is_none() {
|
|
||||||
if a.get_visible_short_aliases().is_some() && a.get_short().is_some() {
|
|
||||||
let mut shorts_and_visible_aliases = a.get_visible_short_aliases().unwrap();
|
|
||||||
shorts_and_visible_aliases.push(a.get_short().unwrap());
|
|
||||||
Some(shorts_and_visible_aliases)
|
|
||||||
} else if a.get_visible_short_aliases().is_none() && a.get_short().is_some() {
|
|
||||||
Some(vec![a.get_short().unwrap()])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets all the long options, their visible aliases and flags of a [`clap::App`].
|
|
||||||
/// Includes `help` and `version` depending on the [`clap::AppSettings`].
|
|
||||||
fn longs_and_visible_aliases(&self, p: &App) -> Vec<String> {
|
|
||||||
debug!("longs: name={}", p.get_name());
|
|
||||||
|
|
||||||
p.get_arguments()
|
|
||||||
.filter_map(|a| {
|
|
||||||
if a.get_index().is_none() {
|
|
||||||
if a.get_visible_aliases().is_some() && a.get_long().is_some() {
|
|
||||||
let mut visible_aliases: Vec<_> = a
|
|
||||||
.get_visible_aliases()
|
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
.collect();
|
|
||||||
visible_aliases.push(a.get_long().unwrap().to_string());
|
|
||||||
Some(visible_aliases)
|
|
||||||
} else if a.get_visible_aliases().is_none() && a.get_long().is_some() {
|
|
||||||
Some(vec![a.get_long().unwrap().to_string()])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets all the flags of a [`clap::App`](App).
|
|
||||||
/// Includes `help` and `version` depending on the [`clap::AppSettings`].
|
|
||||||
fn flags<'help>(&self, p: &App<'help>) -> Vec<Arg<'help>> {
|
|
||||||
debug!("flags: name={}", p.get_name());
|
|
||||||
p.get_flags().cloned().collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
|
|
||||||
struct Foo;
|
|
||||||
|
|
||||||
impl Generator for Foo {
|
|
||||||
fn generate(&self, _: &App, _: &mut dyn Write) {}
|
|
||||||
|
|
||||||
fn file_name(&self, name: &str) -> String {
|
|
||||||
name.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn common() -> App<'static> {
|
|
||||||
let mut app = App::new("myapp")
|
|
||||||
.version("3.0")
|
|
||||||
.subcommand(
|
|
||||||
App::new("test").subcommand(App::new("config")).arg(
|
|
||||||
Arg::new("file")
|
|
||||||
.short('f')
|
|
||||||
.short_alias('c')
|
|
||||||
.visible_short_alias('p')
|
|
||||||
.long("file")
|
|
||||||
.visible_alias("path"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.subcommand(App::new("hello"))
|
|
||||||
.bin_name("my-app");
|
|
||||||
|
|
||||||
app._build();
|
|
||||||
app._build_bin_names();
|
|
||||||
app
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_subcommands() {
|
|
||||||
let app = common();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Foo.subcommands(&app),
|
|
||||||
vec![
|
|
||||||
("test".to_string(), "my-app test".to_string()),
|
|
||||||
("hello".to_string(), "my-app hello".to_string()),
|
|
||||||
("help".to_string(), "my-app help".to_string()),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_all_subcommands() {
|
|
||||||
let app = common();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Foo.all_subcommands(&app),
|
|
||||||
vec![
|
|
||||||
("test".to_string(), "my-app test".to_string()),
|
|
||||||
("hello".to_string(), "my-app hello".to_string()),
|
|
||||||
("help".to_string(), "my-app help".to_string()),
|
|
||||||
("config".to_string(), "my-app test config".to_string()),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_find_subcommand_with_path() {
|
|
||||||
let app = common();
|
|
||||||
let sc_app = Foo.find_subcommand_with_path(&app, "test config".split(' ').collect());
|
|
||||||
|
|
||||||
assert_eq!(sc_app.get_name(), "config");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_flags() {
|
|
||||||
let app = common();
|
|
||||||
let flags = Foo.flags(&app);
|
|
||||||
|
|
||||||
assert_eq!(flags.len(), 2);
|
|
||||||
assert_eq!(flags[0].get_long(), Some("help"));
|
|
||||||
assert_eq!(flags[1].get_long(), Some("version"));
|
|
||||||
|
|
||||||
let sc_flags = Foo.flags(Foo.find_subcommand_with_path(&app, vec!["test"]));
|
|
||||||
|
|
||||||
assert_eq!(sc_flags.len(), 3);
|
|
||||||
assert_eq!(sc_flags[0].get_long(), Some("file"));
|
|
||||||
assert_eq!(sc_flags[1].get_long(), Some("help"));
|
|
||||||
assert_eq!(sc_flags[2].get_long(), Some("version"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_shorts() {
|
|
||||||
let app = common();
|
|
||||||
let shorts = Foo.shorts_and_visible_aliases(&app);
|
|
||||||
|
|
||||||
assert_eq!(shorts.len(), 2);
|
|
||||||
assert_eq!(shorts[0], 'h');
|
|
||||||
assert_eq!(shorts[1], 'V');
|
|
||||||
|
|
||||||
let sc_shorts =
|
|
||||||
Foo.shorts_and_visible_aliases(Foo.find_subcommand_with_path(&app, vec!["test"]));
|
|
||||||
|
|
||||||
assert_eq!(sc_shorts.len(), 4);
|
|
||||||
assert_eq!(sc_shorts[0], 'p');
|
|
||||||
assert_eq!(sc_shorts[1], 'f');
|
|
||||||
assert_eq!(sc_shorts[2], 'h');
|
|
||||||
assert_eq!(sc_shorts[3], 'V');
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_longs() {
|
|
||||||
let app = common();
|
|
||||||
let longs = Foo.longs_and_visible_aliases(&app);
|
|
||||||
|
|
||||||
assert_eq!(longs.len(), 2);
|
|
||||||
assert_eq!(longs[0], "help");
|
|
||||||
assert_eq!(longs[1], "version");
|
|
||||||
|
|
||||||
let sc_longs =
|
|
||||||
Foo.longs_and_visible_aliases(Foo.find_subcommand_with_path(&app, vec!["test"]));
|
|
||||||
|
|
||||||
assert_eq!(sc_longs.len(), 4);
|
|
||||||
assert_eq!(sc_longs[0], "path");
|
|
||||||
assert_eq!(sc_longs[1], "file");
|
|
||||||
assert_eq!(sc_longs[2], "help");
|
|
||||||
assert_eq!(sc_longs[3], "version");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
|
use crate::utils;
|
||||||
use crate::Generator;
|
use crate::Generator;
|
||||||
use clap::*;
|
use clap::*;
|
||||||
|
|
||||||
|
@ -75,8 +76,7 @@ fn all_subcommands(app: &App) -> String {
|
||||||
debug!("all_subcommands");
|
debug!("all_subcommands");
|
||||||
|
|
||||||
let mut subcmds = vec![String::new()];
|
let mut subcmds = vec![String::new()];
|
||||||
let mut scs = Bash
|
let mut scs = utils::all_subcommands(app)
|
||||||
.all_subcommands(app)
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| x.0.clone())
|
.map(|x| x.0.clone())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -101,8 +101,7 @@ fn subcommand_details(app: &App) -> String {
|
||||||
debug!("subcommand_details");
|
debug!("subcommand_details");
|
||||||
|
|
||||||
let mut subcmd_dets = vec![String::new()];
|
let mut subcmd_dets = vec![String::new()];
|
||||||
let mut scs = Bash
|
let mut scs = utils::all_subcommands(app)
|
||||||
.all_subcommands(app)
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| x.1.replace(" ", "__"))
|
.map(|x| x.1.replace(" ", "__"))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -138,7 +137,7 @@ fn subcommand_details(app: &App) -> String {
|
||||||
fn option_details_for_path(app: &App, path: &str) -> String {
|
fn option_details_for_path(app: &App, path: &str) -> String {
|
||||||
debug!("option_details_for_path: path={}", path);
|
debug!("option_details_for_path: path={}", path);
|
||||||
|
|
||||||
let p = Bash.find_subcommand_with_path(app, path.split("__").skip(1).collect());
|
let p = utils::find_subcommand_with_path(app, path.split("__").skip(1).collect());
|
||||||
let mut opts = vec![String::new()];
|
let mut opts = vec![String::new()];
|
||||||
|
|
||||||
for o in p.get_opts() {
|
for o in p.get_opts() {
|
||||||
|
@ -191,17 +190,15 @@ fn vals_for(o: &Arg) -> String {
|
||||||
fn all_options_for_path(app: &App, path: &str) -> String {
|
fn all_options_for_path(app: &App, path: &str) -> String {
|
||||||
debug!("all_options_for_path: path={}", path);
|
debug!("all_options_for_path: path={}", path);
|
||||||
|
|
||||||
let p = Bash.find_subcommand_with_path(app, path.split("__").skip(1).collect());
|
let p = utils::find_subcommand_with_path(app, path.split("__").skip(1).collect());
|
||||||
let scs: Vec<_> = Bash.subcommands(p).iter().map(|x| x.0.clone()).collect();
|
let scs: Vec<_> = utils::subcommands(p).iter().map(|x| x.0.clone()).collect();
|
||||||
|
|
||||||
let opts = format!(
|
let opts = format!(
|
||||||
"{shorts} {longs} {pos} {subcmds}",
|
"{shorts} {longs} {pos} {subcmds}",
|
||||||
shorts = Bash
|
shorts = utils::shorts_and_visible_aliases(p)
|
||||||
.shorts_and_visible_aliases(p)
|
|
||||||
.iter()
|
.iter()
|
||||||
.fold(String::new(), |acc, s| format!("{} -{}", acc, s)),
|
.fold(String::new(), |acc, s| format!("{} -{}", acc, s)),
|
||||||
longs = Bash
|
longs = utils::longs_and_visible_aliases(p)
|
||||||
.longs_and_visible_aliases(p)
|
|
||||||
.iter()
|
.iter()
|
||||||
.fold(String::new(), |acc, l| format!("{} --{}", acc, l)),
|
.fold(String::new(), |acc, l| format!("{} --{}", acc, l)),
|
||||||
pos = p
|
pos = p
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
|
use crate::utils;
|
||||||
use crate::Generator;
|
use crate::Generator;
|
||||||
use crate::INTERNAL_ERROR_MSG;
|
use crate::INTERNAL_ERROR_MSG;
|
||||||
use clap::*;
|
use clap::*;
|
||||||
|
@ -99,7 +100,7 @@ fn generate_inner<'help>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for flag in Elvish.flags(p) {
|
for flag in utils::flags(p) {
|
||||||
if let Some(shorts) = flag.get_short_and_visible_aliases() {
|
if let Some(shorts) = flag.get_short_and_visible_aliases() {
|
||||||
let tooltip = get_tooltip(flag.get_about(), shorts[0]);
|
let tooltip = get_tooltip(flag.get_about(), shorts[0]);
|
||||||
for short in shorts {
|
for short in shorts {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
|
use crate::utils;
|
||||||
use crate::Generator;
|
use crate::Generator;
|
||||||
use clap::*;
|
use clap::*;
|
||||||
|
|
||||||
|
@ -96,7 +97,7 @@ fn gen_fish_inner(root_command: &str, parent_commands: &[&str], app: &App, buffe
|
||||||
buffer.push('\n');
|
buffer.push('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
for flag in Fish.flags(app) {
|
for flag in utils::flags(app) {
|
||||||
let mut template = basic_template.clone();
|
let mut template = basic_template.clone();
|
||||||
|
|
||||||
if let Some(shorts) = flag.get_short_and_visible_aliases() {
|
if let Some(shorts) = flag.get_short_and_visible_aliases() {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
|
use crate::utils;
|
||||||
use crate::Generator;
|
use crate::Generator;
|
||||||
use crate::INTERNAL_ERROR_MSG;
|
use crate::INTERNAL_ERROR_MSG;
|
||||||
use clap::*;
|
use clap::*;
|
||||||
|
@ -115,7 +116,7 @@ fn generate_inner<'help>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for flag in PowerShell.flags(p) {
|
for flag in utils::flags(p) {
|
||||||
if let Some(shorts) = flag.get_short_and_visible_aliases() {
|
if let Some(shorts) = flag.get_short_and_visible_aliases() {
|
||||||
let tooltip = get_tooltip(flag.get_about(), shorts[0]);
|
let tooltip = get_tooltip(flag.get_about(), shorts[0]);
|
||||||
for short in shorts {
|
for short in shorts {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
|
use crate::utils;
|
||||||
use crate::Generator;
|
use crate::Generator;
|
||||||
use crate::INTERNAL_ERROR_MSG;
|
use crate::INTERNAL_ERROR_MSG;
|
||||||
use clap::*;
|
use clap::*;
|
||||||
|
@ -100,7 +101,7 @@ _{bin_name_underscore}_commands() {{
|
||||||
ret.push(parent_text);
|
ret.push(parent_text);
|
||||||
|
|
||||||
// Next we start looping through all the children, grandchildren, etc.
|
// Next we start looping through all the children, grandchildren, etc.
|
||||||
let mut all_subcommands = Zsh.all_subcommands(p);
|
let mut all_subcommands = utils::all_subcommands(p);
|
||||||
|
|
||||||
all_subcommands.sort();
|
all_subcommands.sort();
|
||||||
all_subcommands.dedup();
|
all_subcommands.dedup();
|
||||||
|
@ -216,7 +217,7 @@ fn get_subcommands_of(parent: &App) -> String {
|
||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
let subcommand_names = Zsh.subcommands(parent);
|
let subcommand_names = utils::subcommands(parent);
|
||||||
let mut all_subcommands = vec![];
|
let mut all_subcommands = vec![];
|
||||||
|
|
||||||
for &(ref name, ref bin_name) in &subcommand_names {
|
for &(ref name, ref bin_name) in &subcommand_names {
|
||||||
|
@ -528,7 +529,7 @@ fn write_flags_of(p: &App, p_global: Option<&App>) -> String {
|
||||||
|
|
||||||
let mut ret = vec![];
|
let mut ret = vec![];
|
||||||
|
|
||||||
for f in Zsh.flags(p) {
|
for f in utils::flags(p) {
|
||||||
debug!("write_flags_of:iter: f={}", f.get_name());
|
debug!("write_flags_of:iter: f={}", f.get_name());
|
||||||
|
|
||||||
let help = f.get_about().map_or(String::new(), escape_help);
|
let help = f.get_about().map_or(String::new(), escape_help);
|
||||||
|
|
|
@ -66,6 +66,8 @@ mod macros;
|
||||||
pub mod generators;
|
pub mod generators;
|
||||||
/// Contains supported shells for auto-completion scripts
|
/// Contains supported shells for auto-completion scripts
|
||||||
mod shell;
|
mod shell;
|
||||||
|
/// Helpers for writing generators
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
240
clap_generate/src/utils.rs
Normal file
240
clap_generate/src/utils.rs
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
use clap::{App, Arg};
|
||||||
|
|
||||||
|
/// Gets all subcommands including child subcommands in the form of `("name", "bin_name")`.
|
||||||
|
///
|
||||||
|
/// Subcommand `rustup toolchain install` would be converted to
|
||||||
|
/// `("install", "rustup toolchain install")`.
|
||||||
|
pub fn all_subcommands(app: &App) -> Vec<(String, String)> {
|
||||||
|
let mut subcmds: Vec<_> = subcommands(app);
|
||||||
|
|
||||||
|
for sc_v in app.get_subcommands().map(|s| all_subcommands(s)) {
|
||||||
|
subcmds.extend(sc_v);
|
||||||
|
}
|
||||||
|
|
||||||
|
subcmds
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the subcommand [`clap::App`] from the given [`clap::App`] with the given path.
|
||||||
|
///
|
||||||
|
/// **NOTE:** `path` should not contain the root `bin_name`.
|
||||||
|
pub fn find_subcommand_with_path<'help, 'app>(
|
||||||
|
p: &'app App<'help>,
|
||||||
|
path: Vec<&str>,
|
||||||
|
) -> &'app App<'help> {
|
||||||
|
let mut app = p;
|
||||||
|
|
||||||
|
for sc in path {
|
||||||
|
app = app.find_subcommand(sc).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
app
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets subcommands of [`clap::App`] in the form of `("name", "bin_name")`.
|
||||||
|
///
|
||||||
|
/// Subcommand `rustup toolchain install` would be converted to
|
||||||
|
/// `("install", "rustup toolchain install")`.
|
||||||
|
pub fn subcommands(p: &App) -> Vec<(String, String)> {
|
||||||
|
debug!("subcommands: name={}", p.get_name());
|
||||||
|
debug!("subcommands: Has subcommands...{:?}", p.has_subcommands());
|
||||||
|
|
||||||
|
let mut subcmds = vec![];
|
||||||
|
|
||||||
|
if !p.has_subcommands() {
|
||||||
|
return subcmds;
|
||||||
|
}
|
||||||
|
|
||||||
|
for sc in p.get_subcommands() {
|
||||||
|
let sc_bin_name = sc.get_bin_name().unwrap();
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"subcommands:iter: name={}, bin_name={}",
|
||||||
|
sc.get_name(),
|
||||||
|
sc_bin_name
|
||||||
|
);
|
||||||
|
|
||||||
|
subcmds.push((sc.get_name().to_string(), sc_bin_name.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
subcmds
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets all the short options, their visible aliases and flags of a [`clap::App`].
|
||||||
|
/// Includes `h` and `V` depending on the [`clap::AppSettings`].
|
||||||
|
pub fn shorts_and_visible_aliases(p: &App) -> Vec<char> {
|
||||||
|
debug!("shorts: name={}", p.get_name());
|
||||||
|
|
||||||
|
p.get_arguments()
|
||||||
|
.filter_map(|a| {
|
||||||
|
if a.get_index().is_none() {
|
||||||
|
if a.get_visible_short_aliases().is_some() && a.get_short().is_some() {
|
||||||
|
let mut shorts_and_visible_aliases = a.get_visible_short_aliases().unwrap();
|
||||||
|
shorts_and_visible_aliases.push(a.get_short().unwrap());
|
||||||
|
Some(shorts_and_visible_aliases)
|
||||||
|
} else if a.get_visible_short_aliases().is_none() && a.get_short().is_some() {
|
||||||
|
Some(vec![a.get_short().unwrap()])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets all the long options, their visible aliases and flags of a [`clap::App`].
|
||||||
|
/// Includes `help` and `version` depending on the [`clap::AppSettings`].
|
||||||
|
pub fn longs_and_visible_aliases(p: &App) -> Vec<String> {
|
||||||
|
debug!("longs: name={}", p.get_name());
|
||||||
|
|
||||||
|
p.get_arguments()
|
||||||
|
.filter_map(|a| {
|
||||||
|
if a.get_index().is_none() {
|
||||||
|
if a.get_visible_aliases().is_some() && a.get_long().is_some() {
|
||||||
|
let mut visible_aliases: Vec<_> = a
|
||||||
|
.get_visible_aliases()
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect();
|
||||||
|
visible_aliases.push(a.get_long().unwrap().to_string());
|
||||||
|
Some(visible_aliases)
|
||||||
|
} else if a.get_visible_aliases().is_none() && a.get_long().is_some() {
|
||||||
|
Some(vec![a.get_long().unwrap().to_string()])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets all the flags of a [`clap::App`](App).
|
||||||
|
/// Includes `help` and `version` depending on the [`clap::AppSettings`].
|
||||||
|
pub fn flags<'help>(p: &App<'help>) -> Vec<Arg<'help>> {
|
||||||
|
debug!("flags: name={}", p.get_name());
|
||||||
|
p.get_flags().cloned().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use clap::Arg;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
fn common() -> App<'static> {
|
||||||
|
let mut app = App::new("myapp")
|
||||||
|
.version("3.0")
|
||||||
|
.subcommand(
|
||||||
|
App::new("test").subcommand(App::new("config")).arg(
|
||||||
|
Arg::new("file")
|
||||||
|
.short('f')
|
||||||
|
.short_alias('c')
|
||||||
|
.visible_short_alias('p')
|
||||||
|
.long("file")
|
||||||
|
.visible_alias("path"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.subcommand(App::new("hello"))
|
||||||
|
.bin_name("my-app");
|
||||||
|
|
||||||
|
app._build();
|
||||||
|
app._build_bin_names();
|
||||||
|
app
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_subcommands() {
|
||||||
|
let app = common();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
subcommands(&app),
|
||||||
|
vec![
|
||||||
|
("test".to_string(), "my-app test".to_string()),
|
||||||
|
("hello".to_string(), "my-app hello".to_string()),
|
||||||
|
("help".to_string(), "my-app help".to_string()),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_all_subcommands() {
|
||||||
|
let app = common();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
all_subcommands(&app),
|
||||||
|
vec![
|
||||||
|
("test".to_string(), "my-app test".to_string()),
|
||||||
|
("hello".to_string(), "my-app hello".to_string()),
|
||||||
|
("help".to_string(), "my-app help".to_string()),
|
||||||
|
("config".to_string(), "my-app test config".to_string()),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_subcommand_with_path() {
|
||||||
|
let app = common();
|
||||||
|
let sc_app = find_subcommand_with_path(&app, "test config".split(' ').collect());
|
||||||
|
|
||||||
|
assert_eq!(sc_app.get_name(), "config");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_flags() {
|
||||||
|
let app = common();
|
||||||
|
let actual_flags = flags(&app);
|
||||||
|
|
||||||
|
assert_eq!(actual_flags.len(), 2);
|
||||||
|
assert_eq!(actual_flags[0].get_long(), Some("help"));
|
||||||
|
assert_eq!(actual_flags[1].get_long(), Some("version"));
|
||||||
|
|
||||||
|
let sc_flags = flags(find_subcommand_with_path(&app, vec!["test"]));
|
||||||
|
|
||||||
|
assert_eq!(sc_flags.len(), 3);
|
||||||
|
assert_eq!(sc_flags[0].get_long(), Some("file"));
|
||||||
|
assert_eq!(sc_flags[1].get_long(), Some("help"));
|
||||||
|
assert_eq!(sc_flags[2].get_long(), Some("version"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shorts() {
|
||||||
|
let app = common();
|
||||||
|
let shorts = shorts_and_visible_aliases(&app);
|
||||||
|
|
||||||
|
assert_eq!(shorts.len(), 2);
|
||||||
|
assert_eq!(shorts[0], 'h');
|
||||||
|
assert_eq!(shorts[1], 'V');
|
||||||
|
|
||||||
|
let sc_shorts = shorts_and_visible_aliases(find_subcommand_with_path(&app, vec!["test"]));
|
||||||
|
|
||||||
|
assert_eq!(sc_shorts.len(), 4);
|
||||||
|
assert_eq!(sc_shorts[0], 'p');
|
||||||
|
assert_eq!(sc_shorts[1], 'f');
|
||||||
|
assert_eq!(sc_shorts[2], 'h');
|
||||||
|
assert_eq!(sc_shorts[3], 'V');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_longs() {
|
||||||
|
let app = common();
|
||||||
|
let longs = longs_and_visible_aliases(&app);
|
||||||
|
|
||||||
|
assert_eq!(longs.len(), 2);
|
||||||
|
assert_eq!(longs[0], "help");
|
||||||
|
assert_eq!(longs[1], "version");
|
||||||
|
|
||||||
|
let sc_longs = longs_and_visible_aliases(find_subcommand_with_path(&app, vec!["test"]));
|
||||||
|
|
||||||
|
assert_eq!(sc_longs.len(), 4);
|
||||||
|
assert_eq!(sc_longs[0], "path");
|
||||||
|
assert_eq!(sc_longs[1], "file");
|
||||||
|
assert_eq!(sc_longs[2], "help");
|
||||||
|
assert_eq!(sc_longs[3], "version");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue