Skip to content

Commit

Permalink
feat(arg_aliases): Ability to alias arguments
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Salim Afiune committed Oct 4, 2016
1 parent 4c0ed77 commit 33b5f6e
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 11 deletions.
11 changes: 11 additions & 0 deletions clap-test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![""]);
Expand Down
35 changes: 32 additions & 3 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
50 changes: 50 additions & 0 deletions src/args/arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down
54 changes: 53 additions & 1 deletion src/args/arg_builder/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>>,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(())
}
}
Expand All @@ -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(),
Expand Down Expand Up @@ -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
}
}
}

Expand Down Expand Up @@ -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]");
}
}
8 changes: 8 additions & 0 deletions tests/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 33b5f6e

Please sign in to comment.