mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
feat(arg_aliases): Ability to alias arguments
There are some cases where you need to have an argument to have an alias, an example could be when you depricate one option in favor of another one. Now you are going to be able to alias arguments as follows: ``` Arg::with_name("opt") .long("opt") .short("o") .takes_value(true) .alias("invisible") .visible_alias("visible") ``` Closes #669
This commit is contained in:
parent
4c0ed77601
commit
33b5f6ef2c
7 changed files with 291 additions and 11 deletions
11
clap-test.rs
11
clap-test.rs
|
@ -50,6 +50,17 @@ mod test {
|
|||
assert_eq!(use_stderr, err.use_stderr());
|
||||
}
|
||||
|
||||
pub fn check_subcommand_help(mut a: App, cmd: &str, out: &str) {
|
||||
// We call a get_matches method to cause --help and --version to be built
|
||||
let _ = a.get_matches_from_safe_borrow(vec![""]);
|
||||
let sc = a.p.subcommands.iter().filter(|s| s.p.meta.name == cmd).next().unwrap();
|
||||
|
||||
// Now we check the output of print_help()
|
||||
let mut help = vec![];
|
||||
sc.write_help(&mut help).ok().expect("failed to print help");
|
||||
assert_eq!(str::from_utf8(&help).unwrap(), out);
|
||||
}
|
||||
|
||||
pub fn check_help(mut a: App, out: &str) {
|
||||
// We call a get_matches method to cause --help and --version to be built
|
||||
let _ = a.get_matches_from_safe_borrow(vec![""]);
|
||||
|
|
|
@ -604,7 +604,17 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
// Check to see if parsing a value from an option
|
||||
if let Some(nvo) = needs_val_of {
|
||||
// get the OptBuilder so we can check the settings
|
||||
if let Some(opt) = self.opts.iter().find(|o| &o.name == &nvo) {
|
||||
if let Some(opt) = self.opts
|
||||
.iter()
|
||||
.find(|o| {
|
||||
&o.name == &nvo ||
|
||||
(o.aliases.is_some() &&
|
||||
o.aliases
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|&(a, _)| a == &*nvo))
|
||||
}) {
|
||||
needs_val_of = try!(self.add_val_to_arg(opt, &arg_os, matcher));
|
||||
// get the next value from the iterator
|
||||
continue;
|
||||
|
@ -765,7 +775,17 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
if let Some(a) = needs_val_of {
|
||||
debugln!("needs_val_of={}", a);
|
||||
debug!("Is this an opt...");
|
||||
if let Some(o) = self.opts.iter().find(|o| &o.name == &a) {
|
||||
if let Some(o) = self.opts
|
||||
.iter()
|
||||
.find(|o| {
|
||||
&o.name == &a ||
|
||||
(o.aliases.is_some() &&
|
||||
o.aliases
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|&(n, _)| n == &*a))
|
||||
}) {
|
||||
sdebugln!("Yes");
|
||||
try!(self.validate_required(matcher));
|
||||
reqs_validated = true;
|
||||
|
@ -1213,7 +1233,16 @@ impl<'a, 'b> Parser<'a, 'b>
|
|||
|
||||
if let Some(opt) = self.opts
|
||||
.iter()
|
||||
.find(|v| v.long.is_some() && &*v.long.unwrap() == arg) {
|
||||
.find(|v|
|
||||
(v.long.is_some() &&
|
||||
&*v.long.unwrap() == arg) ||
|
||||
(v.aliases.is_some() &&
|
||||
v.aliases
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|&(n, _)| n == &*arg))
|
||||
) {
|
||||
debugln!("Found valid opt '{}'", opt.to_string());
|
||||
let ret = try!(self.parse_opt(val, opt, matcher));
|
||||
arg_post_processing!(self, opt, matcher);
|
||||
|
|
|
@ -42,6 +42,8 @@ pub struct Arg<'a, 'b>
|
|||
#[doc(hidden)]
|
||||
pub long: Option<&'b str>,
|
||||
#[doc(hidden)]
|
||||
pub aliases: Option<Vec<(&'b str, bool)>>, // (name, visible)
|
||||
#[doc(hidden)]
|
||||
pub help: Option<&'b str>,
|
||||
#[doc(hidden)]
|
||||
pub index: Option<u64>,
|
||||
|
@ -83,6 +85,7 @@ impl<'a, 'b> Default for Arg<'a, 'b> {
|
|||
name: "".as_ref(),
|
||||
short: None,
|
||||
long: None,
|
||||
aliases: None,
|
||||
help: None,
|
||||
index: None,
|
||||
blacklist: None,
|
||||
|
@ -149,6 +152,7 @@ impl<'a, 'b> Arg<'a, 'b> {
|
|||
a = match k.as_str().unwrap() {
|
||||
"short" => yaml_to_str!(a, v, short),
|
||||
"long" => yaml_to_str!(a, v, long),
|
||||
"aliases" => yaml_vec_or_str!(v, a, alias),
|
||||
"help" => yaml_to_str!(a, v, help),
|
||||
"required" => yaml_to_bool!(a, v, required),
|
||||
"takes_value" => yaml_to_bool!(a, v, takes_value),
|
||||
|
@ -409,6 +413,50 @@ impl<'a, 'b> Arg<'a, 'b> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Add docs
|
||||
pub fn alias<S: Into<&'b str>>(mut self, name: S) -> Self {
|
||||
if let Some(ref mut als) = self.aliases {
|
||||
als.push((name.into(), false));
|
||||
} else {
|
||||
self.aliases = Some(vec![(name.into(), false)]);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Add docs
|
||||
pub fn aliases(mut self, names: &[&'b str]) -> Self {
|
||||
if let Some(ref mut als) = self.aliases {
|
||||
for n in names {
|
||||
als.push((n, false));
|
||||
}
|
||||
} else {
|
||||
self.aliases = Some(names.iter().map(|n| (*n, false)).collect::<Vec<_>>());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Add docs
|
||||
pub fn visible_alias<S: Into<&'b str>>(mut self, name: S) -> Self {
|
||||
if let Some(ref mut als) = self.aliases {
|
||||
als.push((name.into(), true));
|
||||
} else {
|
||||
self.aliases = Some(vec![(name.into(), true)]);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Add docs
|
||||
pub fn visible_aliases(mut self, names: &[&'b str]) -> Self {
|
||||
if let Some(ref mut als) = self.aliases {
|
||||
for n in names {
|
||||
als.push((n, true));
|
||||
}
|
||||
} else {
|
||||
self.aliases = Some(names.iter().map(|n| (*n, true)).collect::<Vec<_>>());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the help text of the argument that will be displayed to the user when they print the
|
||||
/// usage/help information.
|
||||
///
|
||||
|
@ -2380,6 +2428,7 @@ impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for Arg<'a, 'b> {
|
|||
name: a.name,
|
||||
short: a.short,
|
||||
long: a.long,
|
||||
aliases: a.aliases.clone(),
|
||||
help: a.help,
|
||||
index: a.index,
|
||||
possible_vals: a.possible_vals.clone(),
|
||||
|
@ -2407,6 +2456,7 @@ impl<'a, 'b> Clone for Arg<'a, 'b> {
|
|||
name: self.name,
|
||||
short: self.short,
|
||||
long: self.long,
|
||||
aliases: self.aliases.clone(),
|
||||
help: self.help,
|
||||
index: self.index,
|
||||
possible_vals: self.possible_vals.clone(),
|
||||
|
|
|
@ -16,6 +16,7 @@ pub struct OptBuilder<'n, 'e> {
|
|||
pub name: &'n str,
|
||||
pub short: Option<char>,
|
||||
pub long: Option<&'e str>,
|
||||
pub aliases: Option<Vec<(&'e str, bool)>>,
|
||||
pub help: Option<&'e str>,
|
||||
pub blacklist: Option<Vec<&'e str>>,
|
||||
pub possible_vals: Option<Vec<&'e str>>,
|
||||
|
@ -39,6 +40,7 @@ impl<'n, 'e> Default for OptBuilder<'n, 'e> {
|
|||
name: "",
|
||||
short: None,
|
||||
long: None,
|
||||
aliases: None,
|
||||
help: None,
|
||||
blacklist: None,
|
||||
possible_vals: None,
|
||||
|
@ -74,6 +76,7 @@ impl<'n, 'e> OptBuilder<'n, 'e> {
|
|||
name: a.name,
|
||||
short: a.short,
|
||||
long: a.long,
|
||||
aliases: a.aliases.clone(),
|
||||
help: a.help,
|
||||
num_vals: a.num_vals,
|
||||
min_vals: a.min_vals,
|
||||
|
@ -157,6 +160,21 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
|
|||
}));
|
||||
}
|
||||
|
||||
// Write aliases such as [aliases: alias, new-alias]
|
||||
if let Some(ref vec) = self.aliases {
|
||||
try!(write!(f, " [aliases: "));
|
||||
let mut it = vec.iter().peekable();
|
||||
while let Some(&(val, b)) = it.next() {
|
||||
if b {
|
||||
try!(write!(f, "{}", val));
|
||||
if it.peek().is_some() {
|
||||
try!(write!(f, ", "));
|
||||
}
|
||||
}
|
||||
}
|
||||
try!(write!(f, "]"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -167,6 +185,7 @@ impl<'n, 'e> Clone for OptBuilder<'n, 'e> {
|
|||
name: self.name,
|
||||
short: self.short,
|
||||
long: self.long,
|
||||
aliases: self.aliases.clone(),
|
||||
help: self.help,
|
||||
blacklist: self.blacklist.clone(),
|
||||
overrides: self.overrides.clone(),
|
||||
|
@ -252,7 +271,19 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
|
|||
true
|
||||
}
|
||||
fn aliases(&self) -> Option<Vec<&'e str>> {
|
||||
None
|
||||
if let Some(ref aliases) = self.aliases {
|
||||
let vis_aliases: Vec<_> =
|
||||
aliases.iter()
|
||||
.filter_map(|&(n, v)| if v { Some(n) } else { None })
|
||||
.collect();
|
||||
if vis_aliases.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(vis_aliases)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,4 +334,25 @@ mod test {
|
|||
|
||||
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optbuilder_display_single_alias() {
|
||||
let mut o = OptBuilder::new("opt");
|
||||
o.long = Some("option");
|
||||
o.aliases = Some(vec![("als", true)]);
|
||||
|
||||
assert_eq!(&*format!("{}", o), "--option <opt> [aliases: als]");
|
||||
}
|
||||
|
||||
fn optbuilder_display_multiple_aliases() {
|
||||
let mut o = OptBuilder::new("opt");
|
||||
o.long = Some("option");
|
||||
o.aliases = Some(vec![
|
||||
("als_not_visible", false),
|
||||
("als2", true),
|
||||
("als3", true),
|
||||
("als4", true)
|
||||
]);
|
||||
assert_eq!(&*format!("{}", o), "--option <opt> [aliases: als2, als3, als4]");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,14 @@ args:
|
|||
help: Tests mutliple values with required delimiter
|
||||
multiple: true
|
||||
require_delimiter: true
|
||||
- singlealias:
|
||||
long: singlealias
|
||||
help: Tests single alias
|
||||
aliases: [alias]
|
||||
- multaliases:
|
||||
long: multaliases
|
||||
help: Tests mutliple aliases
|
||||
aliases: [als1, als2, als3]
|
||||
- minvals2:
|
||||
long: minvals2
|
||||
multiple: true
|
||||
|
|
135
tests/arg_aliases.rs
Normal file
135
tests/arg_aliases.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
extern crate clap;
|
||||
extern crate regex;
|
||||
|
||||
include!("../clap-test.rs");
|
||||
|
||||
use clap::{App, Arg, SubCommand};
|
||||
|
||||
static SC_VISIBLE_ALIAS_HELP: &'static str = "test
|
||||
Some help
|
||||
|
||||
USAGE:
|
||||
test [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--opt <opt> [aliases: visible]";
|
||||
|
||||
static SC_INVISIBLE_ALIAS_HELP: &'static str = "test
|
||||
Some help
|
||||
|
||||
USAGE:
|
||||
test [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--opt <opt> ";
|
||||
|
||||
#[test]
|
||||
fn single_alias_of_option_long() {
|
||||
let a = App::new("single_alias")
|
||||
.arg(Arg::with_name("alias")
|
||||
.long("alias")
|
||||
.takes_value(true)
|
||||
.help("single alias")
|
||||
.alias("new-opt"))
|
||||
.get_matches_from_safe(vec![
|
||||
"", "--new-opt", "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_aliases_of_option_long() {
|
||||
let a = App::new("multiple_aliases")
|
||||
.arg(Arg::with_name("aliases")
|
||||
.long("aliases")
|
||||
.takes_value(true)
|
||||
.help("multiple aliases")
|
||||
.aliases(&vec![
|
||||
"alias1",
|
||||
"alias2",
|
||||
"alias3"
|
||||
]));
|
||||
let long = a.clone().get_matches_from_safe(vec![
|
||||
"", "--aliases", "value"
|
||||
]);
|
||||
assert!(long.is_ok());
|
||||
let long = long.unwrap();
|
||||
|
||||
let als1 = a.clone().get_matches_from_safe(vec![
|
||||
"", "--alias1", "value"
|
||||
]);
|
||||
assert!(als1.is_ok());
|
||||
let als1 = als1.unwrap();
|
||||
|
||||
let als2 = a.clone().get_matches_from_safe(vec![
|
||||
"", "--alias2", "value"
|
||||
]);
|
||||
assert!(als2.is_ok());
|
||||
let als2 = als2.unwrap();
|
||||
|
||||
let als3 = a.clone().get_matches_from_safe(vec![
|
||||
"", "--alias3", "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 alias_on_a_subcommand_option() {
|
||||
let m = App::new("test")
|
||||
.subcommand(SubCommand::with_name("some")
|
||||
.arg(Arg::with_name("test")
|
||||
.short("t")
|
||||
.long("test")
|
||||
.takes_value(true)
|
||||
.alias("opt")
|
||||
.help("testing testing")))
|
||||
.arg(Arg::with_name("other")
|
||||
.long("other")
|
||||
.aliases(&vec!["o1", "o2", "o3"]))
|
||||
.get_matches_from(vec![
|
||||
"test", "some", "--opt", "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_arg_aliases_help_output() {
|
||||
let app = App::new("clap-test")
|
||||
.subcommand(SubCommand::with_name("test")
|
||||
.about("Some help")
|
||||
.arg(Arg::with_name("opt")
|
||||
.long("opt")
|
||||
.takes_value(true)
|
||||
.aliases(&["invisible", "als1", "more"])));
|
||||
test::check_subcommand_help(app, "test", SC_INVISIBLE_ALIAS_HELP);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn visible_arg_aliases_help_output() {
|
||||
let app = App::new("clap-test")
|
||||
.subcommand(SubCommand::with_name("test")
|
||||
.about("Some help")
|
||||
.arg(Arg::with_name("opt")
|
||||
.long("opt")
|
||||
.takes_value(true)
|
||||
.alias("invisible")
|
||||
.visible_alias("visible")));
|
||||
test::check_subcommand_help(app, "test", SC_VISIBLE_ALIAS_HELP);
|
||||
}
|
|
@ -262,13 +262,8 @@ fn no_wrap_help() {
|
|||
|
||||
#[test]
|
||||
fn complex_subcommand_help_output() {
|
||||
let mut a = test::complex_app();
|
||||
let _ = a.get_matches_from_safe_borrow(vec![""]);
|
||||
let sc = a.p.subcommands.iter().filter(|s| s.p.meta.name == "subcmd").next().unwrap();
|
||||
// Now we check the output of print_help()
|
||||
let mut help = vec![];
|
||||
sc.write_help(&mut help).ok().expect("failed to print help");
|
||||
assert_eq!(&*String::from_utf8(help).unwrap(), SC_HELP);
|
||||
let a = test::complex_app();
|
||||
test::check_subcommand_help(a, "subcmd", SC_HELP);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue