diff options
Diffstat (limited to 'vendor/clap_complete/src/generator/utils.rs')
-rw-r--r-- | vendor/clap_complete/src/generator/utils.rs | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/vendor/clap_complete/src/generator/utils.rs b/vendor/clap_complete/src/generator/utils.rs new file mode 100644 index 000000000..b8aaa4bd5 --- /dev/null +++ b/vendor/clap_complete/src/generator/utils.rs @@ -0,0 +1,267 @@ +//! Helpers for writing generators + +use clap::{Arg, Command}; + +/// 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(cmd: &Command) -> Vec<(String, String)> { + let mut subcmds: Vec<_> = subcommands(cmd); + + for sc_v in cmd.get_subcommands().map(all_subcommands) { + subcmds.extend(sc_v); + } + + subcmds +} + +/// Finds the subcommand [`clap::Command`] from the given [`clap::Command`] with the given path. +/// +/// **NOTE:** `path` should not contain the root `bin_name`. +pub fn find_subcommand_with_path<'help, 'cmd>( + p: &'cmd Command<'help>, + path: Vec<&str>, +) -> &'cmd Command<'help> { + let mut cmd = p; + + for sc in path { + cmd = cmd.find_subcommand(sc).unwrap(); + } + + cmd +} + +/// Gets subcommands of [`clap::Command`] in the form of `("name", "bin_name")`. +/// +/// Subcommand `rustup toolchain install` would be converted to +/// `("install", "rustup toolchain install")`. +pub fn subcommands(p: &Command) -> 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::Command`]. +/// Includes `h` and `V` depending on the [`clap::AppSettings`]. +pub fn shorts_and_visible_aliases(p: &Command) -> Vec<char> { + debug!("shorts: name={}", p.get_name()); + + p.get_arguments() + .filter_map(|a| { + if !a.is_positional() { + 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::Command`]. +/// Includes `help` and `version` depending on the [`clap::AppSettings`]. +pub fn longs_and_visible_aliases(p: &Command) -> Vec<String> { + debug!("longs: name={}", p.get_name()); + + p.get_arguments() + .filter_map(|a| { + if !a.is_positional() { + 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::Command`](Command). +/// Includes `help` and `version` depending on the [`clap::AppSettings`]. +pub fn flags<'help>(p: &Command<'help>) -> Vec<Arg<'help>> { + debug!("flags: name={}", p.get_name()); + p.get_arguments() + .filter(|a| !a.is_takes_value_set() && !a.is_positional()) + .cloned() + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + use clap::Arg; + use pretty_assertions::assert_eq; + + fn common_app() -> Command<'static> { + Command::new("myapp") + .subcommand( + Command::new("test").subcommand(Command::new("config")).arg( + Arg::new("file") + .short('f') + .short_alias('c') + .visible_short_alias('p') + .long("file") + .visible_alias("path"), + ), + ) + .subcommand(Command::new("hello")) + .bin_name("my-cmd") + } + + fn built() -> Command<'static> { + let mut cmd = common_app(); + + cmd._build_all(); + cmd + } + + fn built_with_version() -> Command<'static> { + let mut cmd = common_app().version("3.0"); + + cmd._build_all(); + cmd + } + + #[test] + fn test_subcommands() { + let cmd = built_with_version(); + + assert_eq!( + subcommands(&cmd), + vec![ + ("test".to_string(), "my-cmd test".to_string()), + ("hello".to_string(), "my-cmd hello".to_string()), + ("help".to_string(), "my-cmd help".to_string()), + ] + ); + } + + #[test] + fn test_all_subcommands() { + let cmd = built_with_version(); + + assert_eq!( + all_subcommands(&cmd), + vec![ + ("test".to_string(), "my-cmd test".to_string()), + ("hello".to_string(), "my-cmd hello".to_string()), + ("help".to_string(), "my-cmd help".to_string()), + ("config".to_string(), "my-cmd test config".to_string()), + ("help".to_string(), "my-cmd test help".to_string()), + ] + ); + } + + #[test] + fn test_find_subcommand_with_path() { + let cmd = built_with_version(); + let sc_app = find_subcommand_with_path(&cmd, "test config".split(' ').collect()); + + assert_eq!(sc_app.get_name(), "config"); + } + + #[test] + fn test_flags() { + let cmd = built_with_version(); + let actual_flags = flags(&cmd); + + 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(&cmd, vec!["test"])); + + assert_eq!(sc_flags.len(), 2); + assert_eq!(sc_flags[0].get_long(), Some("file")); + assert_eq!(sc_flags[1].get_long(), Some("help")); + } + + #[test] + fn test_flag_subcommand() { + let cmd = built(); + let actual_flags = flags(&cmd); + + assert_eq!(actual_flags.len(), 1); + assert_eq!(actual_flags[0].get_long(), Some("help")); + + let sc_flags = flags(find_subcommand_with_path(&cmd, vec!["test"])); + + assert_eq!(sc_flags.len(), 2); + assert_eq!(sc_flags[0].get_long(), Some("file")); + assert_eq!(sc_flags[1].get_long(), Some("help")); + } + + #[test] + fn test_shorts() { + let cmd = built_with_version(); + let shorts = shorts_and_visible_aliases(&cmd); + + 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(&cmd, vec!["test"])); + + assert_eq!(sc_shorts.len(), 3); + assert_eq!(sc_shorts[0], 'p'); + assert_eq!(sc_shorts[1], 'f'); + assert_eq!(sc_shorts[2], 'h'); + } + + #[test] + fn test_longs() { + let cmd = built_with_version(); + let longs = longs_and_visible_aliases(&cmd); + + 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(&cmd, vec!["test"])); + + assert_eq!(sc_longs.len(), 3); + assert_eq!(sc_longs[0], "path"); + assert_eq!(sc_longs[1], "file"); + assert_eq!(sc_longs[2], "help"); + } +} |