From 33b5f6ef2c9612ecabb31f96b824793e46bfd3dd Mon Sep 17 00:00:00 2001 From: Salim Afiune Date: Fri, 30 Sep 2016 17:54:14 -0400 Subject: [PATCH 1/4] 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 --- clap-test.rs | 11 +++ src/app/parser.rs | 35 ++++++++- src/args/arg.rs | 50 ++++++++++++ src/args/arg_builder/option.rs | 54 ++++++++++++- tests/app.yml | 8 ++ tests/arg_aliases.rs | 135 +++++++++++++++++++++++++++++++++ tests/help.rs | 9 +-- 7 files changed, 291 insertions(+), 11 deletions(-) create mode 100644 tests/arg_aliases.rs diff --git a/clap-test.rs b/clap-test.rs index e3425d60842..0de3790e5bf 100644 --- a/clap-test.rs +++ b/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![""]); diff --git a/src/app/parser.rs b/src/app/parser.rs index 7c896203f74..d0635a3673e 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -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); diff --git a/src/args/arg.rs b/src/args/arg.rs index f8a24610588..3ba090f1a1a 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -42,6 +42,8 @@ pub struct Arg<'a, 'b> #[doc(hidden)] pub long: Option<&'b str>, #[doc(hidden)] + pub aliases: Option>, // (name, visible) + #[doc(hidden)] pub help: Option<&'b str>, #[doc(hidden)] pub index: Option, @@ -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>(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::>()); + } + self + } + + /// Add docs + pub fn visible_alias>(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::>()); + } + 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(), diff --git a/src/args/arg_builder/option.rs b/src/args/arg_builder/option.rs index 646aa1cee3c..438cc820336 100644 --- a/src/args/arg_builder/option.rs +++ b/src/args/arg_builder/option.rs @@ -16,6 +16,7 @@ pub struct OptBuilder<'n, 'e> { pub name: &'n str, pub short: Option, pub long: Option<&'e str>, + pub aliases: Option>, pub help: Option<&'e str>, pub blacklist: Option>, pub possible_vals: Option>, @@ -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> { - 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 "); } + + #[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 [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 [aliases: als2, als3, als4]"); + } } diff --git a/tests/app.yml b/tests/app.yml index 0e78f5bb4b5..1588a00c641 100644 --- a/tests/app.yml +++ b/tests/app.yml @@ -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 diff --git a/tests/arg_aliases.rs b/tests/arg_aliases.rs new file mode 100644 index 00000000000..d794f9de157 --- /dev/null +++ b/tests/arg_aliases.rs @@ -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 [aliases: visible]"; + +static SC_INVISIBLE_ALIAS_HELP: &'static str = "test +Some help + +USAGE: + test [OPTIONS] + +OPTIONS: + --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); +} diff --git a/tests/help.rs b/tests/help.rs index 5cc72fbb549..5d8fa633a16 100644 --- a/tests/help.rs +++ b/tests/help.rs @@ -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); } From e5d9760bea8bd2cca643c7664567b262282848fa Mon Sep 17 00:00:00 2001 From: Salim Afiune Date: Mon, 3 Oct 2016 09:35:13 -0400 Subject: [PATCH 2/4] Added Arg::(visible_)alias(es) docs Signed-off-by: Salim Afiune --- src/args/arg.rs | 74 +++++++++++++++++++++++++++++++++++++++++--- tests/arg_aliases.rs | 12 +++++++ 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/src/args/arg.rs b/src/args/arg.rs index 3ba090f1a1a..d6adef98ac5 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -413,7 +413,25 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Add docs + /// 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("myprog") + /// .arg(Arg::with_name("test") + /// .long("test") + /// .alias("alias") + /// .takes_value(true)) + /// .get_matches_from(vec!["myprog", "--alias", "cool"]); + /// assert!(m.is_present("test")); + /// assert_eq!(m.value_of("test"), Some("cool")); + /// ``` + /// [`Arg`]: ./struct.Arg.html pub fn alias>(mut self, name: S) -> Self { if let Some(ref mut als) = self.aliases { als.push((name.into(), false)); @@ -423,7 +441,24 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Add docs + /// 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("myprog") + /// .arg(Arg::with_name("test") + /// .aliases(&["do-stuff", "do-tests", "tests"]) + /// .help("the file to add") + /// .required(false)) + /// .get_matches_from(vec!["myprog", "--do-tests"]); + /// assert!(m.is_present("test")); + /// ``` + /// [`Arg`]: ./struct.Arg.html pub fn aliases(mut self, names: &[&'b str]) -> Self { if let Some(ref mut als) = self.aliases { for n in names { @@ -435,7 +470,24 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Add docs + /// 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("myprog") + /// .arg(Arg::with_name("test") + /// .visible_alias("something-awesome") + /// .long("test") + /// .takes_value(true)) + /// .get_matches_from(vec!["myprog", "--something-awesome", "coffee"]); + /// assert!(m.is_present("test")); + /// assert_eq!(m.value_of("test"), Some("coffee")); + /// ``` + /// [`Arg`]: ./struct.Arg.html + /// [`App::alias`]: ./struct.Arg.html#method.alias pub fn visible_alias>(mut self, name: S) -> Self { if let Some(ref mut als) = self.aliases { als.push((name.into(), true)); @@ -445,7 +497,21 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Add docs + /// 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("myprog") + /// .arg(Arg::with_name("test") + /// .visible_aliases(&["something", "awesome", "cool"])) + /// .get_matches_from(vec!["myprog", "--awesome"]); + /// assert!(m.is_present("test")); + /// ``` + /// [`Arg`]: ./struct.Arg.html + /// [`App::aliases`]: ./struct.Arg.html#method.aliases pub fn visible_aliases(mut self, names: &[&'b str]) -> Self { if let Some(ref mut als) = self.aliases { for n in names { diff --git a/tests/arg_aliases.rs b/tests/arg_aliases.rs index d794f9de157..2895128afcc 100644 --- a/tests/arg_aliases.rs +++ b/tests/arg_aliases.rs @@ -133,3 +133,15 @@ fn visible_arg_aliases_help_output() { .visible_alias("visible"))); test::check_subcommand_help(app, "test", SC_VISIBLE_ALIAS_HELP); } + +#[test] +fn visible_arg_flag_aliases() { + let a = App::new("test") + .arg(Arg::with_name("opt") + .long("opt") + .aliases(&["invisible", "set", "of", "aliases"])) + .get_matches_from_safe(vec!["", "--aliases"]); + assert!(a.is_ok()); + let a = a.unwrap(); + assert!(a.is_present("opt")); +} From 40d6dac973927dded6ab423481634ef47ee7bfd7 Mon Sep 17 00:00:00 2001 From: Salim Afiune Date: Mon, 3 Oct 2016 11:01:04 -0400 Subject: [PATCH 3/4] feat(flag_aliases): Ability to alias flags Added same alias funtionality for flags, now you can do: ``` Arg::with_name("flg") .long("flag") .short("f") .alias("not_visible_flag") .visible_alias("awesome_v_flag") ``` Signed-off-by: Salim Afiune --- src/app/parser.rs | 11 +++- src/args/arg.rs | 2 + src/args/arg_builder/flag.rs | 61 ++++++++++++++++++++-- src/args/arg_builder/option.rs | 1 + tests/arg_aliases.rs | 95 +++++++++++++++++++++++++++------- 5 files changed, 146 insertions(+), 24 deletions(-) diff --git a/src/app/parser.rs b/src/app/parser.rs index d0635a3673e..099bc30a699 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -1250,7 +1250,16 @@ impl<'a, 'b> Parser<'a, 'b> return Ok(ret); } else if let Some(flag) = self.flags .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 flag '{}'", flag.to_string()); // Only flags could be help or version, and we need to check the raw long // so this is the first point to check diff --git a/src/args/arg.rs b/src/args/arg.rs index d6adef98ac5..e1a35887020 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -452,6 +452,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// # use clap::{App, Arg}; /// let m = App::new("myprog") /// .arg(Arg::with_name("test") + /// .long("test") /// .aliases(&["do-stuff", "do-tests", "tests"]) /// .help("the file to add") /// .required(false)) @@ -506,6 +507,7 @@ impl<'a, 'b> Arg<'a, 'b> { /// # use clap::{App, Arg}; /// let m = App::new("myprog") /// .arg(Arg::with_name("test") + /// .long("test") /// .visible_aliases(&["something", "awesome", "cool"])) /// .get_matches_from(vec!["myprog", "--awesome"]); /// assert!(m.is_present("test")); diff --git a/src/args/arg_builder/flag.rs b/src/args/arg_builder/flag.rs index 471993e4618..5f99017b5be 100644 --- a/src/args/arg_builder/flag.rs +++ b/src/args/arg_builder/flag.rs @@ -17,6 +17,7 @@ use args::settings::{ArgFlags, ArgSettings}; pub struct FlagBuilder<'n, 'e> { pub name: &'n str, pub long: Option<&'e str>, + pub aliases: Option>, pub help: Option<&'e str>, pub blacklist: Option>, pub requires: Option>, @@ -31,6 +32,7 @@ impl<'n, 'e> Default for FlagBuilder<'n, 'e> { FlagBuilder { name: "", long: None, + aliases: None, help: None, blacklist: None, requires: None, @@ -68,6 +70,7 @@ impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for FlagBuilder<'a, 'b> { name: a.name, short: a.short, long: a.long, + aliases: a.aliases.clone(), help: a.help, blacklist: a.blacklist.clone(), overrides: a.overrides.clone(), @@ -81,10 +84,27 @@ impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for FlagBuilder<'a, 'b> { impl<'n, 'e> Display for FlagBuilder<'n, 'e> { fn fmt(&self, f: &mut Formatter) -> Result { if let Some(l) = self.long { - write!(f, "--{}", l) + try!(write!(f, "--{}", l)); } else { - write!(f, "-{}", self.short.unwrap()) + try!(write!(f, "-{}", self.short.unwrap())); } + + // 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(()) } } @@ -94,6 +114,7 @@ impl<'n, 'e> Clone for FlagBuilder<'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(), @@ -169,7 +190,19 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> { self.long.is_some() } fn aliases(&self) -> Option> { - 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 + } } } @@ -197,4 +230,26 @@ mod test { assert_eq!(&*format!("{}", f2), "-f"); } + + #[test] + fn flagbuilder_display_single_alias() { + let mut f = FlagBuilder::new("flg"); + f.long = Some("flag"); + f.aliases = Some(vec![("als", true)]); + + assert_eq!(&*format!("{}", f), "--flag [aliases: als]"); + } + + #[test] + fn flagbuilder_display_multiple_aliases() { + let mut f = FlagBuilder::new("flg"); + f.short = Some('f'); + f.aliases = Some(vec![ + ("alias_not_visible", false), + ("f2", true), + ("f3", true), + ("f4", true) + ]); + assert_eq!(&*format!("{}", f), "-f [aliases: f2, f3, f4]"); + } } diff --git a/src/args/arg_builder/option.rs b/src/args/arg_builder/option.rs index 438cc820336..501375f552d 100644 --- a/src/args/arg_builder/option.rs +++ b/src/args/arg_builder/option.rs @@ -344,6 +344,7 @@ mod test { assert_eq!(&*format!("{}", o), "--option [aliases: als]"); } + #[test] fn optbuilder_display_multiple_aliases() { let mut o = OptBuilder::new("opt"); o.long = Some("option"); diff --git a/tests/arg_aliases.rs b/tests/arg_aliases.rs index 2895128afcc..68620a60de5 100644 --- a/tests/arg_aliases.rs +++ b/tests/arg_aliases.rs @@ -9,22 +9,30 @@ static SC_VISIBLE_ALIAS_HELP: &'static str = "test Some help USAGE: - test [OPTIONS] + test [FLAGS] [OPTIONS] + +FLAGS: + -f, --flag + flag with aliases [aliases: v_flg, flag2, flg3] OPTIONS: - --opt [aliases: visible]"; + -o, --opt + help for option with alias [aliases: visible]"; static SC_INVISIBLE_ALIAS_HELP: &'static str = "test Some help USAGE: - test [OPTIONS] + test [FLAGS] [OPTIONS] + +FLAGS: + -f, --flag flag with aliases OPTIONS: - --opt "; + -o, --opt help for option with alias"; #[test] -fn single_alias_of_option_long() { +fn single_alias_of_option() { let a = App::new("single_alias") .arg(Arg::with_name("alias") .long("alias") @@ -41,7 +49,7 @@ fn single_alias_of_option_long() { } #[test] -fn multiple_aliases_of_option_long() { +fn multiple_aliases_of_option() { let a = App::new("multiple_aliases") .arg(Arg::with_name("aliases") .long("aliases") @@ -86,6 +94,49 @@ fn multiple_aliases_of_option_long() { assert_eq!(als3.value_of("aliases").unwrap(), "value"); } +#[test] +fn single_alias_of_flag() { + let a = App::new("test") + .arg(Arg::with_name("flag") + .long("flag") + .alias("alias")) + .get_matches_from_safe(vec!["", "--alias"]); + assert!(a.is_ok()); + let a = a.unwrap(); + assert!(a.is_present("flag")); +} + +#[test] +fn multiple_aliases_of_flag() { + let a = App::new("test") + .arg(Arg::with_name("flag") + .long("flag") + .aliases(&["invisible", + "set", "of", + "cool", "aliases"])); + + let flag = a.clone().get_matches_from_safe(vec!["", "--flag"]); + assert!(flag.is_ok()); + let flag = flag.unwrap(); + + let inv = a.clone().get_matches_from_safe(vec!["", "--invisible"]); + assert!(inv.is_ok()); + let inv = inv.unwrap(); + + let cool = a.clone().get_matches_from_safe(vec!["", "--cool"]); + assert!(cool.is_ok()); + let cool = cool.unwrap(); + + let als = a.clone().get_matches_from_safe(vec!["", "--aliases"]); + assert!(als.is_ok()); + let als = als.unwrap(); + + assert!(flag.is_present("flag")); + assert!(inv.is_present("flag")); + assert!(cool.is_present("flag")); + assert!(als.is_present("flag")); +} + #[test] fn alias_on_a_subcommand_option() { let m = App::new("test") @@ -112,36 +163,40 @@ fn alias_on_a_subcommand_option() { #[test] fn invisible_arg_aliases_help_output() { let app = App::new("clap-test") + .author("Salim Afiune") .subcommand(SubCommand::with_name("test") .about("Some help") .arg(Arg::with_name("opt") .long("opt") + .short("o") + .help("help for option with alias") .takes_value(true) - .aliases(&["invisible", "als1", "more"]))); + .aliases(&["invisible", "als1", "more"])) + .arg(Arg::with_name("flg") + .long("flag") + .short("f") + .help("flag with aliases") + .aliases(&["invisible", "flg1", "anyway"]))); test::check_subcommand_help(app, "test", SC_INVISIBLE_ALIAS_HELP); } #[test] fn visible_arg_aliases_help_output() { let app = App::new("clap-test") + .author("Salim Afiune") .subcommand(SubCommand::with_name("test") .about("Some help") .arg(Arg::with_name("opt") .long("opt") + .short("o") + .help("help for option with alias") .takes_value(true) .alias("invisible") - .visible_alias("visible"))); + .visible_alias("visible")) + .arg(Arg::with_name("flg") + .long("flag") + .short("f") + .help("flag with aliases") + .visible_aliases(&["v_flg", "flag2", "flg3"]))); test::check_subcommand_help(app, "test", SC_VISIBLE_ALIAS_HELP); } - -#[test] -fn visible_arg_flag_aliases() { - let a = App::new("test") - .arg(Arg::with_name("opt") - .long("opt") - .aliases(&["invisible", "set", "of", "aliases"])) - .get_matches_from_safe(vec!["", "--aliases"]); - assert!(a.is_ok()); - let a = a.unwrap(); - assert!(a.is_present("opt")); -} From 905d3b97775e1208645919beeda9e23d4b9c3231 Mon Sep 17 00:00:00 2001 From: Salim Afiune Date: Tue, 4 Oct 2016 09:54:45 -0400 Subject: [PATCH 4/4] Fix linting errors from rustc 1.14.0 The new version of rustc 1.14.0-nightly (144af3e97 2016-10-02) has new linting warnings/errors. This commit fixes them. Signed-off-by: Salim Afiune --- src/app/help.rs | 22 +++++++++++----------- src/app/mod.rs | 2 +- src/app/parser.rs | 10 +++++----- src/suggestions.rs | 2 +- tests/arg_aliases.rs | 18 +++++------------- 5 files changed, 23 insertions(+), 31 deletions(-) diff --git a/src/app/help.rs b/src/app/help.rs index dc07944af7d..1bd23f45b66 100644 --- a/src/app/help.rs +++ b/src/app/help.rs @@ -212,8 +212,8 @@ impl<'a> Help<'a> { } } let mut first = true; - for (_, btm) in ord_m.into_iter() { - for (_, arg) in btm.into_iter() { + for (_, btm) in ord_m { + for arg in btm.values() { if !first { try!(self.writer.write(b"\n")); } else { @@ -380,11 +380,11 @@ impl<'a> Help<'a> { help.push_str(h); &*help }; - if help.contains("\n") { - if let Some(part) = help.split("\n").next() { + if help.contains('\n') { + if let Some(part) = help.split('\n').next() { try!(write!(self.writer, "{}", part)); } - for part in help.split("\n").skip(1) { + for part in help.split('\n').skip(1) { try!(write!(self.writer, "\n{}", part)); } } else { @@ -464,11 +464,11 @@ impl<'a> Help<'a> { help.push_str(&*spec_vals); &*help }; - if help.contains("\n") { - if let Some(part) = help.split("\n").next() { + if help.contains('\n') { + if let Some(part) = help.split('\n').next() { try!(write!(self.writer, "{}", part)); } - for part in help.split("\n").skip(1) { + for part in help.split('\n').skip(1) { try!(write!(self.writer, "\n")); if nlh || force_next_line { try!(write!(self.writer, "{}{}{}", TAB, TAB, TAB)); @@ -616,14 +616,14 @@ impl<'a> Help<'a> { } let mut first = true; - for (_, btm) in ord_m.into_iter() { - for (_, sc) in btm.into_iter() { + for (_, btm) in ord_m { + for sc in btm.values() { if !first { try!(self.writer.write(b"\n")); } else { first = false; } - try!(self.write_arg(&sc, longest)); + try!(self.write_arg(sc, longest)); } } Ok(()) diff --git a/src/app/mod.rs b/src/app/mod.rs index 86d73a0fb92..d95261143be 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -928,7 +928,7 @@ impl<'a, 'b> App<'a, 'b> { pub fn subcommands(mut self, subcmds: I) -> Self where I: IntoIterator> { - for subcmd in subcmds.into_iter() { + for subcmd in subcmds { self.p.add_subcommand(subcmd); } self diff --git a/src/app/parser.rs b/src/app/parser.rs index 099bc30a699..4a17e2bb513 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -355,13 +355,13 @@ impl<'a, 'b> Parser<'a, 'b> grps.dedup(); let mut args_in_groups = vec![]; for g in &grps { - for a in self.arg_names_in_group(g).into_iter() { + for a in self.arg_names_in_group(g) { args_in_groups.push(a); } } let mut pmap = BTreeMap::new(); - for p in c_pos.into_iter() { + for p in c_pos { if matcher.is_some() && matcher.as_ref().unwrap().contains(p) { continue; } @@ -382,7 +382,7 @@ impl<'a, 'b> Parser<'a, 'b> } macro_rules! write_arg { ($i:expr, $m:ident, $v:ident, $r:ident, $aig:ident) => { - for f in $v.into_iter() { + for f in $v { if $m.is_some() && $m.as_ref().unwrap().contains(f) || $aig.contains(&f) { continue; } @@ -393,13 +393,13 @@ impl<'a, 'b> Parser<'a, 'b> write_arg!(self.flags.iter(), matcher, c_flags, ret_val, args_in_groups); write_arg!(self.opts.iter(), matcher, c_opt, ret_val, args_in_groups); let mut g_vec = vec![]; - for g in grps.into_iter() { + for g in grps { let g_string = self.args_in_group(g) .join("|"); g_vec.push(format!("<{}>", &g_string[..g_string.len()])); } g_vec.dedup(); - for g in g_vec.into_iter() { + for g in g_vec { ret_val.push_back(g); } diff --git a/src/suggestions.rs b/src/suggestions.rs index 9633de7bc36..d11c6646391 100644 --- a/src/suggestions.rs +++ b/src/suggestions.rs @@ -17,7 +17,7 @@ pub fn did_you_mean<'a, T, I>(v: &str, possible_values: I) -> Option<&'a str> { let mut candidate: Option<(f64, &str)> = None; - for pv in possible_values.into_iter() { + for pv in possible_values { let confidence = strsim::jaro_winkler(v, pv.as_ref()); if confidence > 0.8 && (candidate.is_none() || (candidate.as_ref().unwrap().0 < confidence)) { diff --git a/tests/arg_aliases.rs b/tests/arg_aliases.rs index 68620a60de5..9c1c419ea79 100644 --- a/tests/arg_aliases.rs +++ b/tests/arg_aliases.rs @@ -12,12 +12,10 @@ USAGE: test [FLAGS] [OPTIONS] FLAGS: - -f, --flag - flag with aliases [aliases: v_flg, flag2, flg3] + -f, --flag [aliases: v_flg, flag2, flg3] OPTIONS: - -o, --opt - help for option with alias [aliases: visible]"; + -o, --opt [aliases: visible]"; static SC_INVISIBLE_ALIAS_HELP: &'static str = "test Some help @@ -26,10 +24,10 @@ USAGE: test [FLAGS] [OPTIONS] FLAGS: - -f, --flag flag with aliases + -f, --flag OPTIONS: - -o, --opt help for option with alias"; + -o, --opt "; #[test] fn single_alias_of_option() { @@ -169,13 +167,9 @@ fn invisible_arg_aliases_help_output() { .arg(Arg::with_name("opt") .long("opt") .short("o") - .help("help for option with alias") .takes_value(true) .aliases(&["invisible", "als1", "more"])) - .arg(Arg::with_name("flg") - .long("flag") - .short("f") - .help("flag with aliases") + .arg(Arg::from_usage("-f, --flag") .aliases(&["invisible", "flg1", "anyway"]))); test::check_subcommand_help(app, "test", SC_INVISIBLE_ALIAS_HELP); } @@ -189,14 +183,12 @@ fn visible_arg_aliases_help_output() { .arg(Arg::with_name("opt") .long("opt") .short("o") - .help("help for option with alias") .takes_value(true) .alias("invisible") .visible_alias("visible")) .arg(Arg::with_name("flg") .long("flag") .short("f") - .help("flag with aliases") .visible_aliases(&["v_flg", "flag2", "flg3"]))); test::check_subcommand_help(app, "test", SC_VISIBLE_ALIAS_HELP); }