mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 23:02:31 +00:00
Merge #1901
1901: implement Arg::short_alias and Arg::short_aliases r=pksunkara a=connorskees Co-authored-by: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com>
This commit is contained in:
commit
33c63c6e29
7 changed files with 431 additions and 7 deletions
|
@ -67,6 +67,7 @@ pub struct Arg<'help> {
|
|||
pub(crate) short: Option<char>,
|
||||
pub(crate) long: Option<&'help str>,
|
||||
pub(crate) aliases: Option<Vec<(&'help str, bool)>>, // (name, visible)
|
||||
pub(crate) short_aliases: Vec<(char, bool)>, // (name, visible)
|
||||
pub(crate) disp_ord: usize,
|
||||
pub(crate) unified_ord: usize,
|
||||
pub(crate) possible_vals: Option<Vec<&'help str>>,
|
||||
|
@ -195,6 +196,7 @@ impl<'help> Arg<'help> {
|
|||
"short" => yaml_to_char!(a, v, short),
|
||||
"long" => yaml_to_str!(a, v, long),
|
||||
"aliases" => yaml_vec_or_str!(v, a, alias),
|
||||
"short_aliases" => yaml_to_chars!(a, v, short_aliases),
|
||||
"about" => yaml_to_str!(a, v, about),
|
||||
"long_about" => yaml_to_str!(a, v, long_about),
|
||||
"help" => yaml_to_str!(a, v, about),
|
||||
|
@ -364,6 +366,36 @@ impl<'help> Arg<'help> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Allows adding a [`Arg`] alias, which function as "hidden" arguments that
|
||||
/// automatically dispatch as if this argument was used. This is more efficient, and easier
|
||||
/// than creating multiple hidden arguments as one only needs to check for the existence of
|
||||
/// this command, and not all variants.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg};
|
||||
/// let m = App::new("prog")
|
||||
/// .arg(Arg::with_name("test")
|
||||
/// .short('t')
|
||||
/// .short_alias('e')
|
||||
/// .takes_value(true))
|
||||
/// .get_matches_from(vec![
|
||||
/// "prog", "-e", "cool"
|
||||
/// ]);
|
||||
/// assert!(m.is_present("test"));
|
||||
/// assert_eq!(m.value_of("test"), Some("cool"));
|
||||
/// ```
|
||||
/// [`Arg`]: ./struct.Arg.html
|
||||
pub fn short_alias(mut self, name: char) -> Self {
|
||||
if name == '-' {
|
||||
panic!("short alias name cannot be `-`");
|
||||
}
|
||||
|
||||
self.short_aliases.push((name, false));
|
||||
self
|
||||
}
|
||||
|
||||
/// Allows adding [`Arg`] aliases, which function as "hidden" arguments that
|
||||
/// automatically dispatch as if this argument was used. This is more efficient, and easier
|
||||
/// than creating multiple hidden subcommands as one only needs to check for the existence of
|
||||
|
@ -396,6 +428,37 @@ impl<'help> Arg<'help> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Allows adding [`Arg`] aliases, which function as "hidden" arguments that
|
||||
/// automatically dispatch as if this argument was used. This is more efficient, and easier
|
||||
/// than creating multiple hidden subcommands as one only needs to check for the existence of
|
||||
/// this command, and not all variants.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg};
|
||||
/// let m = App::new("prog")
|
||||
/// .arg(Arg::with_name("test")
|
||||
/// .short('t')
|
||||
/// .short_aliases(&['e', 's'])
|
||||
/// .help("the file to add")
|
||||
/// .required(false))
|
||||
/// .get_matches_from(vec![
|
||||
/// "prog", "-s"
|
||||
/// ]);
|
||||
/// assert!(m.is_present("test"));
|
||||
/// ```
|
||||
/// [`Arg`]: ./struct.Arg.html
|
||||
pub fn short_aliases(mut self, names: &[char]) -> Self {
|
||||
for s in names {
|
||||
if s == &'-' {
|
||||
panic!("short alias name cannot be `-`");
|
||||
}
|
||||
self.short_aliases.push((*s, false));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Allows adding a [`Arg`] alias that functions exactly like those defined with
|
||||
/// [`Arg::alias`], except that they are visible inside the help message.
|
||||
///
|
||||
|
@ -425,6 +488,35 @@ impl<'help> Arg<'help> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Allows adding a [`Arg`] alias that functions exactly like those defined with
|
||||
/// [`Arg::alias`], except that they are visible inside the help message.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg};
|
||||
/// let m = App::new("prog")
|
||||
/// .arg(Arg::with_name("test")
|
||||
/// .long("test")
|
||||
/// .visible_short_alias('t')
|
||||
/// .takes_value(true))
|
||||
/// .get_matches_from(vec![
|
||||
/// "prog", "-t", "coffee"
|
||||
/// ]);
|
||||
/// assert!(m.is_present("test"));
|
||||
/// assert_eq!(m.value_of("test"), Some("coffee"));
|
||||
/// ```
|
||||
/// [`Arg`]: ./struct.Arg.html
|
||||
/// [`App::alias`]: ./struct.Arg.html#method.short_alias
|
||||
pub fn visible_short_alias(mut self, name: char) -> Self {
|
||||
if name == '-' {
|
||||
panic!("short alias name cannot be `-`");
|
||||
}
|
||||
|
||||
self.short_aliases.push((name, true));
|
||||
self
|
||||
}
|
||||
|
||||
/// Allows adding multiple [`Arg`] aliases that functions exactly like those defined
|
||||
/// with [`Arg::aliases`], except that they are visible inside the help message.
|
||||
///
|
||||
|
@ -454,6 +546,34 @@ impl<'help> Arg<'help> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Allows adding multiple [`Arg`] aliases that functions exactly like those defined
|
||||
/// with [`Arg::aliases`], except that they are visible inside the help message.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg};
|
||||
/// let m = App::new("prog")
|
||||
/// .arg(Arg::with_name("test")
|
||||
/// .long("test")
|
||||
/// .visible_short_aliases(&['t', 'e']))
|
||||
/// .get_matches_from(vec![
|
||||
/// "prog", "-t"
|
||||
/// ]);
|
||||
/// assert!(m.is_present("test"));
|
||||
/// ```
|
||||
/// [`Arg`]: ./struct.Arg.html
|
||||
/// [`App::aliases`]: ./struct.Arg.html#method.short_aliases
|
||||
pub fn visible_short_aliases(mut self, names: &[char]) -> Self {
|
||||
for n in names {
|
||||
if n == &'-' {
|
||||
panic!("short alias name cannot be `-`");
|
||||
}
|
||||
self.short_aliases.push((*n, true));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// See [`Arg::about`](./struct.Arg.html#method.about)
|
||||
#[deprecated(since = "3.0.0", note = "Please use `about` method instead")]
|
||||
#[inline]
|
||||
|
@ -4363,10 +4483,10 @@ impl<'help> fmt::Debug for Arg<'help> {
|
|||
"Arg {{ id: {:X?}, name: {:?}, help: {:?}, long_help: {:?}, conflicts_with: {:?}, \
|
||||
settings: {:?}, required_unless: {:?}, overrides_with: {:?}, groups: {:?}, \
|
||||
requires: {:?}, requires_ifs: {:?}, short: {:?}, index: {:?}, long: {:?}, \
|
||||
aliases: {:?}, possible_values: {:?}, value_names: {:?}, number_of_values: {:?}, \
|
||||
max_values: {:?}, min_values: {:?}, value_delimiter: {:?}, default_value_ifs: {:?}, \
|
||||
value_terminator: {:?}, display_order: {:?}, env: {:?}, unified_ord: {:?}, \
|
||||
default_value: {:?}, validator: {}, validator_os: {} \
|
||||
aliases: {:?}, short_aliases: {:?}, possible_values: {:?}, value_names: {:?}, \
|
||||
number_of_values: {:?}, max_values: {:?}, min_values: {:?}, value_delimiter: {:?}, \
|
||||
default_value_ifs: {:?}, value_terminator: {:?}, display_order: {:?}, env: {:?}, \
|
||||
unified_ord: {:?}, default_value: {:?}, validator: {}, validator_os: {} \
|
||||
}}",
|
||||
self.id,
|
||||
self.name,
|
||||
|
@ -4383,6 +4503,7 @@ impl<'help> fmt::Debug for Arg<'help> {
|
|||
self.index,
|
||||
self.long,
|
||||
self.aliases,
|
||||
self.short_aliases,
|
||||
self.possible_vals,
|
||||
self.val_names,
|
||||
self.num_vals,
|
||||
|
@ -4444,6 +4565,23 @@ mod test {
|
|||
assert_eq!(&*format!("{}", f), "-f");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flag_display_single_short_alias() {
|
||||
let mut f = Arg::with_name("flg");
|
||||
f.short = Some('a');
|
||||
f.short_aliases = vec![('b', true)];
|
||||
|
||||
assert_eq!(&*format!("{}", f), "-a")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flag_display_multiple_short_aliases() {
|
||||
let mut f = Arg::with_name("flg");
|
||||
f.short = Some('a');
|
||||
f.short_aliases = vec![('b', false), ('c', true), ('d', true), ('e', true)];
|
||||
assert_eq!(&*format!("{}", f), "-a");
|
||||
}
|
||||
|
||||
// Options
|
||||
|
||||
#[test]
|
||||
|
@ -4496,6 +4634,27 @@ mod test {
|
|||
assert_eq!(&*format!("{}", o), "--option <opt>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_display_single_short_alias() {
|
||||
let o = Arg::with_name("opt")
|
||||
.takes_value(true)
|
||||
.short('a')
|
||||
.visible_short_alias('b');
|
||||
|
||||
assert_eq!(&*format!("{}", o), "-a <opt>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_display_multiple_short_aliases() {
|
||||
let o = Arg::with_name("opt")
|
||||
.short('a')
|
||||
.takes_value(true)
|
||||
.visible_short_aliases(&['b', 'c', 'd'])
|
||||
.short_alias('e');
|
||||
|
||||
assert_eq!(&*format!("{}", o), "-a <opt>");
|
||||
}
|
||||
|
||||
// Positionals
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -87,6 +87,28 @@ macro_rules! yaml_char {
|
|||
}};
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
macro_rules! yaml_chars {
|
||||
($v:expr) => {{
|
||||
&$v.as_vec()
|
||||
.unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a list", $v))
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
s.as_str()
|
||||
.unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", s))
|
||||
})
|
||||
.map(|s| {
|
||||
let mut chars = s.chars();
|
||||
let c = chars.next().expect("short aliases must be a single char");
|
||||
if chars.next().is_some() {
|
||||
panic!("short aliases must be a single char");
|
||||
}
|
||||
c
|
||||
})
|
||||
.collect::<Vec<char>>()
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
macro_rules! yaml_str {
|
||||
($v:expr) => {{
|
||||
|
@ -102,6 +124,13 @@ macro_rules! yaml_to_char {
|
|||
}};
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
macro_rules! yaml_to_chars {
|
||||
($a:ident, $v:ident, $c:ident) => {{
|
||||
$a.$c(yaml_chars!($v))
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml")]
|
||||
macro_rules! yaml_to_str {
|
||||
($a:ident, $v:ident, $c:ident) => {{
|
||||
|
|
|
@ -140,6 +140,9 @@ fn _get_keys(arg: &Arg) -> Vec<KeyType> {
|
|||
}
|
||||
|
||||
let mut keys = vec![];
|
||||
for short in arg.short_aliases.iter().map(|(c, _)| KeyType::Short(*c)) {
|
||||
keys.push(short);
|
||||
}
|
||||
if let Some(c) = arg.short {
|
||||
keys.push(KeyType::Short(c));
|
||||
}
|
||||
|
|
|
@ -500,6 +500,26 @@ impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
|
|||
spec_vals.push(format!(" [aliases: {}]", als));
|
||||
}
|
||||
}
|
||||
|
||||
if !a.short_aliases.is_empty() {
|
||||
debug!(
|
||||
"Help::spec_vals: Found short aliases...{:?}",
|
||||
a.short_aliases
|
||||
);
|
||||
|
||||
let als = a
|
||||
.short_aliases
|
||||
.iter()
|
||||
.filter(|&als| als.1) // visible
|
||||
.map(|&als| als.0.to_string()) // name
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
if !als.is_empty() {
|
||||
spec_vals.push(format!("[short aliases: {}]", als));
|
||||
}
|
||||
}
|
||||
|
||||
if !self.hide_pv && !a.is_set(ArgSettings::HidePossibleValues) {
|
||||
if let Some(ref pv) = a.possible_vals {
|
||||
debug!("Help::spec_vals: Found possible vals...{:?}", pv);
|
||||
|
|
204
tests/arg_aliases_short.rs
Normal file
204
tests/arg_aliases_short.rs
Normal file
|
@ -0,0 +1,204 @@
|
|||
mod utils;
|
||||
|
||||
use clap::{App, Arg};
|
||||
|
||||
static SC_VISIBLE_ALIAS_HELP: &str = "ct-test 1.2
|
||||
Some help
|
||||
|
||||
USAGE:
|
||||
ct test [FLAGS] [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-f, --flag [aliases: flag1] [short aliases: a, b, 🦆]
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-o, --opt <opt> [short aliases: v]";
|
||||
|
||||
static SC_INVISIBLE_ALIAS_HELP: &str = "ct-test 1.2
|
||||
Some help
|
||||
|
||||
USAGE:
|
||||
ct test [FLAGS] [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-f, --flag
|
||||
-h, --help Prints help information
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
-o, --opt <opt> ";
|
||||
|
||||
#[test]
|
||||
fn single_short_alias_of_option() {
|
||||
let a = App::new("single_alias")
|
||||
.arg(
|
||||
Arg::with_name("alias")
|
||||
.long("alias")
|
||||
.takes_value(true)
|
||||
.about("single short alias")
|
||||
.short_alias('a'),
|
||||
)
|
||||
.try_get_matches_from(vec!["", "-a", "cool"]);
|
||||
assert!(a.is_ok());
|
||||
let a = a.unwrap();
|
||||
assert!(a.is_present("alias"));
|
||||
assert_eq!(a.value_of("alias").unwrap(), "cool");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_short_aliases_of_option() {
|
||||
let a = App::new("multiple_aliases").arg(
|
||||
Arg::with_name("aliases")
|
||||
.long("aliases")
|
||||
.takes_value(true)
|
||||
.about("multiple aliases")
|
||||
.short_aliases(&['1', '2', '3']),
|
||||
);
|
||||
let long = a
|
||||
.clone()
|
||||
.try_get_matches_from(vec!["", "--aliases", "value"]);
|
||||
assert!(long.is_ok());
|
||||
let long = long.unwrap();
|
||||
|
||||
let als1 = a.clone().try_get_matches_from(vec!["", "-1", "value"]);
|
||||
assert!(als1.is_ok());
|
||||
let als1 = als1.unwrap();
|
||||
|
||||
let als2 = a.clone().try_get_matches_from(vec!["", "-2", "value"]);
|
||||
assert!(als2.is_ok());
|
||||
let als2 = als2.unwrap();
|
||||
|
||||
let als3 = a.clone().try_get_matches_from(vec!["", "-3", "value"]);
|
||||
assert!(als3.is_ok());
|
||||
let als3 = als3.unwrap();
|
||||
|
||||
assert!(long.is_present("aliases"));
|
||||
assert!(als1.is_present("aliases"));
|
||||
assert!(als2.is_present("aliases"));
|
||||
assert!(als3.is_present("aliases"));
|
||||
assert_eq!(long.value_of("aliases").unwrap(), "value");
|
||||
assert_eq!(als1.value_of("aliases").unwrap(), "value");
|
||||
assert_eq!(als2.value_of("aliases").unwrap(), "value");
|
||||
assert_eq!(als3.value_of("aliases").unwrap(), "value");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_short_alias_of_flag() {
|
||||
let a = App::new("test")
|
||||
.arg(Arg::with_name("flag").long("flag").short_alias('f'))
|
||||
.try_get_matches_from(vec!["", "-f"]);
|
||||
assert!(a.is_ok());
|
||||
let a = a.unwrap();
|
||||
assert!(a.is_present("flag"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_short_aliases_of_flag() {
|
||||
let a = App::new("test").arg(
|
||||
Arg::with_name("flag")
|
||||
.long("flag")
|
||||
.short_aliases(&['a', 'b', 'c', 'd', 'e']),
|
||||
);
|
||||
|
||||
let flag = a.clone().try_get_matches_from(vec!["", "--flag"]);
|
||||
assert!(flag.is_ok());
|
||||
let flag = flag.unwrap();
|
||||
|
||||
let als1 = a.clone().try_get_matches_from(vec!["", "-a"]);
|
||||
assert!(als1.is_ok());
|
||||
let als1 = als1.unwrap();
|
||||
|
||||
let als2 = a.clone().try_get_matches_from(vec!["", "-b"]);
|
||||
assert!(als2.is_ok());
|
||||
let als2 = als2.unwrap();
|
||||
|
||||
let als3 = a.clone().try_get_matches_from(vec!["", "-c"]);
|
||||
assert!(als3.is_ok());
|
||||
let als3 = als3.unwrap();
|
||||
|
||||
assert!(flag.is_present("flag"));
|
||||
assert!(als1.is_present("flag"));
|
||||
assert!(als2.is_present("flag"));
|
||||
assert!(als3.is_present("flag"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn short_alias_on_a_subcommand_option() {
|
||||
let m = App::new("test")
|
||||
.subcommand(
|
||||
App::new("some").arg(
|
||||
Arg::with_name("test")
|
||||
.short('t')
|
||||
.long("test")
|
||||
.takes_value(true)
|
||||
.short_alias('o')
|
||||
.about("testing testing"),
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("other")
|
||||
.long("other")
|
||||
.short_aliases(&['1', '2', '3']),
|
||||
)
|
||||
.get_matches_from(vec!["test", "some", "-o", "awesome"]);
|
||||
|
||||
assert!(m.subcommand_matches("some").is_some());
|
||||
let sub_m = m.subcommand_matches("some").unwrap();
|
||||
assert!(sub_m.is_present("test"));
|
||||
assert_eq!(sub_m.value_of("test").unwrap(), "awesome");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invisible_short_arg_aliases_help_output() {
|
||||
let app = App::new("ct").author("Salim Afiune").subcommand(
|
||||
App::new("test")
|
||||
.about("Some help")
|
||||
.version("1.2")
|
||||
.arg(
|
||||
Arg::with_name("opt")
|
||||
.long("opt")
|
||||
.short('o')
|
||||
.takes_value(true)
|
||||
.short_aliases(&['a', 'b', 'c']),
|
||||
)
|
||||
.arg(Arg::from("-f, --flag").short_aliases(&['a', 'b', 'c'])),
|
||||
);
|
||||
assert!(utils::compare_output(
|
||||
app,
|
||||
"ct test --help",
|
||||
SC_INVISIBLE_ALIAS_HELP,
|
||||
false
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn visible_short_arg_aliases_help_output() {
|
||||
let app = App::new("ct").author("Salim Afiune").subcommand(
|
||||
App::new("test")
|
||||
.about("Some help")
|
||||
.version("1.2")
|
||||
.arg(
|
||||
Arg::with_name("opt")
|
||||
.long("opt")
|
||||
.short('o')
|
||||
.takes_value(true)
|
||||
.short_alias('i')
|
||||
.visible_short_alias('v'),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("flg")
|
||||
.long("flag")
|
||||
.short('f')
|
||||
.visible_alias("flag1")
|
||||
.visible_short_aliases(&['a', 'b', '🦆']),
|
||||
),
|
||||
);
|
||||
assert!(utils::compare_output(
|
||||
app,
|
||||
"ct test --help",
|
||||
SC_VISIBLE_ALIAS_HELP,
|
||||
false
|
||||
));
|
||||
}
|
10
tests/fixtures/app.yml
vendored
10
tests/fixtures/app.yml
vendored
|
@ -83,6 +83,16 @@ args:
|
|||
long: multaliases
|
||||
about: Tests mutliple aliases
|
||||
aliases: [als1, als2, als3]
|
||||
- singleshortalias:
|
||||
long: singleshortalias
|
||||
about: Tests single short alias
|
||||
short_aliases: [a]
|
||||
required_if:
|
||||
- [multvalsmo, two]
|
||||
- multshortaliases:
|
||||
long: multshortaliases
|
||||
about: Tests mutliple short aliases
|
||||
short_aliases: [a, b, c]
|
||||
- minvals2:
|
||||
long: minvals2
|
||||
multiple: true
|
||||
|
|
|
@ -25,9 +25,8 @@ fn help_message() {
|
|||
let mut help_buffer = Vec::new();
|
||||
app.write_help(&mut help_buffer).unwrap();
|
||||
let help_string = String::from_utf8(help_buffer).unwrap();
|
||||
assert!(
|
||||
help_string.contains("-h, --help prints help with a nonstandard description\n")
|
||||
);
|
||||
assert!(help_string
|
||||
.contains("-h, --help prints help with a nonstandard description\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Reference in a new issue