perf: refactor to remove unneeded vectors and allocations and checks for significant performance increases

Building an `App` struct with a fair number of args/flags/switches, etc. (used ripgrep as test case)
went from taking ~21,000 ns to ~13,000ns.
This commit is contained in:
Kevin K 2017-02-20 23:51:20 -05:00
parent b55cae5fdb
commit 0efa411963
No known key found for this signature in database
GPG key ID: 17218E4B3692F01A
14 changed files with 971 additions and 872 deletions

View file

@ -9,11 +9,12 @@ mod test {
fn compare<S, S2>(l: S, r: S2) -> bool fn compare<S, S2>(l: S, r: S2) -> bool
where S: AsRef<str>, where S: AsRef<str>,
S2: AsRef<str> { S2: AsRef<str>
{
let re = Regex::new("\x1b[^m]*m").unwrap(); let re = Regex::new("\x1b[^m]*m").unwrap();
// Strip out any mismatching \r character on windows that might sneak in on either side // Strip out any mismatching \r character on windows that might sneak in on either side
let left = re.replace_all(&l.as_ref().trim().replace("\r", "")[..], "").into_owned(); let left = re.replace_all(&l.as_ref().trim().replace("\r", "")[..], "");
let right = re.replace_all(&r.as_ref().trim().replace("\r", "")[..], "").into_owned(); let right = re.replace_all(&r.as_ref().trim().replace("\r", "")[..], "");
let b = left == right; let b = left == right;
if !b { if !b {
println!(""); println!("");

View file

@ -19,11 +19,14 @@ macro_rules! remove_overriden {
}; };
($_self:ident, $name:expr) => { ($_self:ident, $name:expr) => {
debugln!("remove_overriden!;"); debugln!("remove_overriden!;");
if let Some(ref o) = $_self.opts.iter().filter(|o| o.b.name == *$name).next() { if let Some(o) = $_self.opts.iter() .find(|o| o.b.name == *$name) {
remove_overriden!(@arg $_self, o); remove_overriden!(@arg $_self, o);
} else if let Some(ref f) = $_self.flags.iter().filter(|f| f.b.name == *$name).next() { } else if let Some(f) = $_self.flags.iter() .find(|f| f.b.name == *$name) {
remove_overriden!(@arg $_self, f); remove_overriden!(@arg $_self, f);
} else if let Some(p) = $_self.positionals.values().filter(|p| p.b.name == *$name).next() { } else {
let p = $_self.positionals.values()
.find(|p| p.b.name == *$name)
.expect(INTERNAL_ERROR_MSG);
remove_overriden!(@arg $_self, p); remove_overriden!(@arg $_self, p);
} }
}; };
@ -69,21 +72,20 @@ macro_rules! arg_post_processing {
} }
} }
$me.blacklist.extend(bl); $me.blacklist.extend_from_slice(bl);
vec_remove_all!($me.overrides, bl.iter()); vec_remove_all!($me.overrides, bl.iter());
vec_remove_all!($me.required, bl.iter()); // vec_remove_all!($me.required, bl.iter());
} else { sdebugln!("No"); } } else { sdebugln!("No"); }
// Add all required args which aren't already found in matcher to the master // Add all required args which aren't already found in matcher to the master
// list // list
debug!("arg_post_processing!: Does '{}' have requirements...", $arg.to_string()); debug!("arg_post_processing!: Does '{}' have requirements...", $arg.to_string());
if let Some(reqs) = $arg.requires() { if let Some(reqs) = $arg.requires() {
for n in reqs.iter().filter(|&&(val, _)| val.is_none()).map(|&(_, name)| name) { for n in reqs.iter()
if $matcher.contains(&n) { .filter(|&&(val, _)| val.is_none())
sdebugln!("\tYes '{}' but it's already met", n); .filter(|&&(_, req)| !$matcher.contains(&req))
continue; .map(|&(_, name)| name) {
} else { sdebugln!("\tYes '{}'", n); }
$me.required.push(n); $me.required.push(n);
} }
} else { sdebugln!("No"); } } else { sdebugln!("No"); }
@ -96,9 +98,9 @@ macro_rules! _handle_group_reqs{
($me:ident, $arg:ident) => ({ ($me:ident, $arg:ident) => ({
use args::AnyArg; use args::AnyArg;
debugln!("_handle_group_reqs!;"); debugln!("_handle_group_reqs!;");
for grp in $me.groups.values() { for grp in $me.groups.iter() {
let found = if grp.args.contains(&$arg.name()) { let found = if grp.args.contains(&$arg.name()) {
vec_remove!($me.required, &$arg.name()); // vec_remove!($me.required, &$arg.name());
if let Some(ref reqs) = grp.requires { if let Some(ref reqs) = grp.requires {
debugln!("_handle_group_reqs!: Adding {:?} to the required list", reqs); debugln!("_handle_group_reqs!: Adding {:?} to the required list", reqs);
$me.required.extend(reqs); $me.required.extend(reqs);
@ -126,18 +128,6 @@ macro_rules! _handle_group_reqs{
}) })
} }
macro_rules! validate_multiples {
($_self:ident, $a:ident, $m:ident) => {
debugln!("validate_multiples!;");
if $m.contains(&$a.b.name) && !$a.b.is_set(ArgSettings::Multiple) {
// Not the first time, and we don't allow multiples
return Err(Error::unexpected_multiple_usage($a,
&*$_self.create_current_usage($m, None),
$_self.color()))
}
};
}
macro_rules! parse_positional { macro_rules! parse_positional {
( (
$_self:ident, $_self:ident,
@ -147,12 +137,11 @@ macro_rules! parse_positional {
$matcher:ident $matcher:ident
) => { ) => {
debugln!("parse_positional!;"); debugln!("parse_positional!;");
validate_multiples!($_self, $p, $matcher);
if !$_self.is_set(TrailingValues) && if !$_self.is_set(AS::TrailingValues) &&
($_self.is_set(TrailingVarArg) && ($_self.is_set(AS::TrailingVarArg) &&
$pos_counter == $_self.positionals.len()) { $pos_counter == $_self.positionals.len()) {
$_self.settings.set(TrailingValues); $_self.settings.set(AS::TrailingValues);
} }
let _ = try!($_self.add_val_to_arg($p, &$arg_os, $matcher)); let _ = try!($_self.add_val_to_arg($p, &$arg_os, $matcher));
@ -166,89 +155,3 @@ macro_rules! parse_positional {
} }
}; };
} }
macro_rules! find_from {
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
let mut ret = None;
for k in $matcher.arg_names() {
if let Some(f) = find_by_name!($_self, &k, flags, iter) {
if let Some(ref v) = f.$from() {
if v.contains($arg_name) {
ret = Some(f.to_string());
}
}
}
if let Some(o) = find_by_name!($_self, &k, opts, iter) {
if let Some(ref v) = o.$from() {
if v.contains(&$arg_name) {
ret = Some(o.to_string());
}
}
}
if let Some(pos) = find_by_name!($_self, &k, positionals, values) {
if let Some(ref v) = pos.$from() {
if v.contains($arg_name) {
ret = Some(pos.b.name.to_owned());
}
}
}
}
ret
}};
}
macro_rules! find_name_from {
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
let mut ret = None;
for k in $matcher.arg_names() {
if let Some(f) = find_by_name!($_self, &k, flags, iter) {
if let Some(ref v) = f.$from() {
if v.contains($arg_name) {
ret = Some(f.b.name);
}
}
}
if let Some(o) = find_by_name!($_self, &k, opts, iter) {
if let Some(ref v) = o.$from() {
if v.contains(&$arg_name) {
ret = Some(o.b.name);
}
}
}
if let Some(pos) = find_by_name!($_self, &k, positionals, values) {
if let Some(ref v) = pos.$from() {
if v.contains($arg_name) {
ret = Some(pos.b.name);
}
}
}
}
ret
}};
}
// Finds an arg by name
macro_rules! find_by_name {
($_self:ident, $name:expr, $what:ident, $how:ident) => {
$_self.$what.$how().find(|o| &o.b.name == $name)
}
}
// Finds an option including if it's aliasesed
macro_rules! find_by_long {
($_self:ident, $long:expr, $what:ident) => {
$_self.$what
.iter()
.filter(|o| o.s.long.is_some())
.find(|o| {
&&o.s.long.unwrap() == &$long ||
(o.s.aliases.is_some() &&
o.s
.aliases
.as_ref()
.unwrap()
.iter()
.any(|&(alias, _)| &&alias == &$long))
})
}
}

File diff suppressed because it is too large Load diff

View file

@ -1335,7 +1335,7 @@ impl<'a, 'b> Arg<'a, 'b> {
/// .long("other") /// .long("other")
/// .takes_value(true)) /// .takes_value(true))
/// .get_matches_from_safe(vec![ /// .get_matches_from_safe(vec![
/// "prog", "--other", "not-special" /// "prog", "--other", "not-special"
/// ]); /// ]);
/// ///
/// assert!(res.is_ok()); // We didn't use --other=special, so "cfg" wasn't required /// assert!(res.is_ok()); // We didn't use --other=special, so "cfg" wasn't required
@ -1355,7 +1355,7 @@ impl<'a, 'b> Arg<'a, 'b> {
/// .long("other") /// .long("other")
/// .takes_value(true)) /// .takes_value(true))
/// .get_matches_from_safe(vec![ /// .get_matches_from_safe(vec![
/// "prog", "--other", "special" /// "prog", "--other", "special"
/// ]); /// ]);
/// ///
/// assert!(res.is_err()); /// assert!(res.is_err());
@ -1721,7 +1721,7 @@ impl<'a, 'b> Arg<'a, 'b> {
/// .short("v")) /// .short("v"))
/// .get_matches_from(vec![ /// .get_matches_from(vec![
/// "prog", "-v", "-v", "-v" // note, -vvv would have same result /// "prog", "-v", "-v", "-v" // note, -vvv would have same result
/// ]); /// ]);
/// ///
/// assert!(m.is_present("verbose")); /// assert!(m.is_present("verbose"));
/// assert_eq!(m.occurrences_of("verbose"), 3); /// assert_eq!(m.occurrences_of("verbose"), 3);
@ -1852,7 +1852,7 @@ impl<'a, 'b> Arg<'a, 'b> {
/// [`number_of_values`]). /// [`number_of_values`]).
/// ///
/// **NOTE:** This setting only applies to [options] and [positional arguments] /// **NOTE:** This setting only applies to [options] and [positional arguments]
/// ///
/// **NOTE:** When the terminator is passed in on the command line, it is **not** stored as one /// **NOTE:** When the terminator is passed in on the command line, it is **not** stored as one
/// of the vaues /// of the vaues
/// ///
@ -2965,12 +2965,10 @@ impl<'a, 'b> Arg<'a, 'b> {
/// ``` /// ```
/// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value
/// [`Arg::default_value`]: ./struct.Arg.html#method.default_value /// [`Arg::default_value`]: ./struct.Arg.html#method.default_value
pub fn default_value_if(self, pub fn default_value_if(self, arg: &'a str, val: Option<&'b str>, default: &'b str) -> Self {
arg: &'a str, self.default_value_if_os(arg,
val: Option<&'b str>, val.map(str::as_bytes).map(OsStr::from_bytes),
default: &'b str) OsStr::from_bytes(default.as_bytes()))
-> Self {
self.default_value_if_os(arg, val.map(str::as_bytes).map(OsStr::from_bytes), OsStr::from_bytes(default.as_bytes()))
} }
/// Provides a conditional default value in the exact same manner as [`Arg::default_value_if`] /// Provides a conditional default value in the exact same manner as [`Arg::default_value_if`]
@ -2978,10 +2976,10 @@ impl<'a, 'b> Arg<'a, 'b> {
/// [`Arg::default_value_if`]: ./struct.Arg.html#method.default_value_if /// [`Arg::default_value_if`]: ./struct.Arg.html#method.default_value_if
/// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html
pub fn default_value_if_os(mut self, pub fn default_value_if_os(mut self,
arg: &'a str, arg: &'a str,
val: Option<&'b OsStr>, val: Option<&'b OsStr>,
default: &'b OsStr) default: &'b OsStr)
-> Self { -> Self {
self.setb(ArgSettings::TakesValue); self.setb(ArgSettings::TakesValue);
if let Some(ref mut vm) = self.v.default_vals_ifs { if let Some(ref mut vm) = self.v.default_vals_ifs {
let l = vm.len(); let l = vm.len();
@ -3080,12 +3078,14 @@ impl<'a, 'b> Arg<'a, 'b> {
/// [`Arg::default_value`]: ./struct.Arg.html#method.default_value /// [`Arg::default_value`]: ./struct.Arg.html#method.default_value
pub fn default_value_ifs(mut self, ifs: &[(&'a str, Option<&'b str>, &'b str)]) -> Self { pub fn default_value_ifs(mut self, ifs: &[(&'a str, Option<&'b str>, &'b str)]) -> Self {
for &(arg, val, default) in ifs { for &(arg, val, default) in ifs {
self = self.default_value_if_os(arg, val.map(str::as_bytes).map(OsStr::from_bytes), OsStr::from_bytes(default.as_bytes())); self = self.default_value_if_os(arg,
val.map(str::as_bytes).map(OsStr::from_bytes),
OsStr::from_bytes(default.as_bytes()));
} }
self self
} }
/// Provides multiple conditional default values in the exact same manner as /// Provides multiple conditional default values in the exact same manner as
/// [`Arg::default_value_ifs`] only using [`OsStr`]s instead. /// [`Arg::default_value_ifs`] only using [`OsStr`]s instead.
/// [`Arg::default_value_ifs`]: ./struct.Arg.html#method.default_value_ifs /// [`Arg::default_value_ifs`]: ./struct.Arg.html#method.default_value_ifs
/// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html

View file

@ -22,9 +22,7 @@ impl<'e> Default for Switched<'e> {
} }
impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Switched<'e> { impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Switched<'e> {
fn from(a: &'z Arg<'n, 'e>) -> Self { fn from(a: &'z Arg<'n, 'e>) -> Self { a.s.clone() }
a.s.clone()
}
} }
impl<'e> Clone for Switched<'e> { impl<'e> Clone for Switched<'e> {

View file

@ -40,7 +40,7 @@ impl<'a> ArgMatcher<'a> {
gma.vals.insert(i, v.clone()); gma.vals.insert(i, v.clone());
} }
gma gma
}); });
if sma.vals.is_empty() { if sma.vals.is_empty() {
for (i, v) in &vals { for (i, v) in &vals {
sma.vals.insert(i, v.clone()); sma.vals.insert(i, v.clone());

View file

@ -125,21 +125,7 @@ complete -F _{name} -o bashdefault -o default {name}
let mut p = self.p; let mut p = self.p;
for sc in path.split("__").skip(1) { for sc in path.split("__").skip(1) {
debugln!("BashGen::option_details_for_path:iter: sc={}", sc); debugln!("BashGen::option_details_for_path:iter: sc={}", sc);
p = &p.subcommands p = &find_subcmd!(p, sc).unwrap().p;
.iter()
.find(|s| {
s.p.meta.name == sc ||
(s.p.meta.aliases.is_some() &&
s.p
.meta
.aliases
.as_ref()
.unwrap()
.iter()
.any(|&(n, _)| n == sc))
})
.unwrap()
.p;
} }
let mut opts = String::new(); let mut opts = String::new();
for o in p.opts() { for o in p.opts() {
@ -214,28 +200,12 @@ complete -F _{name} -o bashdefault -o default {name}
let mut p = self.p; let mut p = self.p;
for sc in path.split("__").skip(1) { for sc in path.split("__").skip(1) {
debugln!("BashGen::all_options_for_path:iter: sc={}", sc); debugln!("BashGen::all_options_for_path:iter: sc={}", sc);
p = &p.subcommands p = &find_subcmd!(p, sc).unwrap().p;
.iter()
.find(|s| {
s.p.meta.name == sc ||
(s.p.meta.aliases.is_some() &&
s.p
.meta
.aliases
.as_ref()
.unwrap()
.iter()
.any(|&(n, _)| n == sc))
})
.unwrap()
.p;
} }
let mut opts = p.short_list.iter().fold(String::new(), |acc, s| format!("{} -{}", acc, s)); let mut opts = shorts!(p).fold(String::new(), |acc, s| format!("{} -{}", acc, s));
opts = format!("{} {}", opts = format!("{} {}",
opts, opts,
p.long_list longs!(p).fold(String::new(), |acc, l| format!("{} --{}", acc, l)));
.iter()
.fold(String::new(), |acc, l| format!("{} --{}", acc, l)));
opts = format!("{} {}", opts = format!("{} {}",
opts, opts,
p.positionals p.positionals

View file

@ -19,10 +19,7 @@ impl<'a, 'b> PowerShellGen<'a, 'b> {
let (subcommands_detection_cases, subcommands_cases) = generate_inner(self.p, ""); let (subcommands_detection_cases, subcommands_cases) = generate_inner(self.p, "");
let mut bin_names = vec![ let mut bin_names = vec![bin_name.to_string(), format!("./{0}", bin_name)];
bin_name.to_string(),
format!("./{0}", bin_name),
];
if cfg!(windows) { if cfg!(windows) {
bin_names.push(format!("{0}.exe", bin_name)); bin_names.push(format!("{0}.exe", bin_name));
bin_names.push(format!(r".\{0}", bin_name)); bin_names.push(format!(r".\{0}", bin_name));
@ -92,10 +89,10 @@ fn generate_inner<'a, 'b>(p: &Parser<'a, 'b>, previous_command_name: &str) -> (S
for subcommand in &p.subcommands { for subcommand in &p.subcommands {
completions.push_str(&format!("'{}', ", &subcommand.p.meta.name)); completions.push_str(&format!("'{}', ", &subcommand.p.meta.name));
} }
for short in &p.short_list { for short in shorts!(p) {
completions.push_str(&format!("'-{}', ", short)); completions.push_str(&format!("'-{}', ", short));
} }
for long in &p.long_list { for long in longs!(p) {
completions.push_str(&format!("'--{}', ", long)); completions.push_str(&format!("'--{}', ", long));
} }

View file

@ -498,11 +498,11 @@ macro_rules! crate_name {
/// Provided separator is for the [`crate_authors!`](macro.crate_authors.html) macro, /// Provided separator is for the [`crate_authors!`](macro.crate_authors.html) macro,
/// refer to the documentation therefor. /// refer to the documentation therefor.
/// ///
/// **NOTE:** Changing the values in your `Cargo.toml` does not trigger a re-build automatically, /// **NOTE:** Changing the values in your `Cargo.toml` does not trigger a re-build automatically,
/// and therefore won't change the generated output until you recompile. /// and therefore won't change the generated output until you recompile.
/// ///
/// **Pro Tip:** In some cases you can "trick" the compiler into triggering a rebuild when your /// **Pro Tip:** In some cases you can "trick" the compiler into triggering a rebuild when your
/// `Cargo.toml` is changed by including this in your `src/main.rs` file /// `Cargo.toml` is changed by including this in your `src/main.rs` file
/// `include_str!("../Cargo.toml");` /// `include_str!("../Cargo.toml");`
/// ///
/// # Examples /// # Examples
@ -542,47 +542,47 @@ macro_rules! app_from_crate {
/// # #[macro_use] /// # #[macro_use]
/// # extern crate clap; /// # extern crate clap;
/// # fn main() { /// # fn main() {
/// let matches = clap_app!(myapp => /// let matches = clap_app!(myapp =>
/// (version: "1.0") /// (version: "1.0")
/// (author: "Kevin K. <kbknapp@gmail.com>") /// (author: "Kevin K. <kbknapp@gmail.com>")
/// (about: "Does awesome things") /// (about: "Does awesome things")
/// (@arg CONFIG: -c --config +takes_value "Sets a custom config file") /// (@arg CONFIG: -c --config +takes_value "Sets a custom config file")
/// (@arg INPUT: +required "Sets the input file to use") /// (@arg INPUT: +required "Sets the input file to use")
/// (@arg debug: -d ... "Sets the level of debugging information") /// (@arg debug: -d ... "Sets the level of debugging information")
/// (@subcommand test => /// (@subcommand test =>
/// (about: "controls testing features") /// (about: "controls testing features")
/// (version: "1.3") /// (version: "1.3")
/// (author: "Someone E. <someone_else@other.com>") /// (author: "Someone E. <someone_else@other.com>")
/// (@arg verbose: -v --verbose "Print test information verbosely") /// (@arg verbose: -v --verbose "Print test information verbosely")
/// ) /// )
/// ); /// );
/// # } /// # }
/// ``` /// ```
/// # Shorthand Syntax for Args /// # Shorthand Syntax for Args
/// ///
/// * A single hyphen followed by a character (such as `-c`) sets the [`Arg::short`] /// * A single hyphen followed by a character (such as `-c`) sets the [`Arg::short`]
/// * A double hyphen followed by a character or word (such as `--config`) sets [`Arg::long`] /// * A double hyphen followed by a character or word (such as `--config`) sets [`Arg::long`]
/// * Three dots (`...`) sets [`Arg::multiple(true)`] /// * Three dots (`...`) sets [`Arg::multiple(true)`]
/// * Angled brackets after either a short or long will set [`Arg::value_name`] and /// * Angled brackets after either a short or long will set [`Arg::value_name`] and
/// `Arg::required(true)` such as `--config <FILE>` = `Arg::value_name("FILE")` and /// `Arg::required(true)` such as `--config <FILE>` = `Arg::value_name("FILE")` and
/// `Arg::required(true) /// `Arg::required(true)
/// * Square brackets after either a short or long will set [`Arg::value_name`] and /// * Square brackets after either a short or long will set [`Arg::value_name`] and
/// `Arg::required(false)` such as `--config [FILE]` = `Arg::value_name("FILE")` and /// `Arg::required(false)` such as `--config [FILE]` = `Arg::value_name("FILE")` and
/// `Arg::required(false) /// `Arg::required(false)
/// * There are short hand syntaxes for Arg methods that accept booleans /// * There are short hand syntaxes for Arg methods that accept booleans
/// * A plus sign will set that method to `true` such as `+required` = `Arg::required(true)` /// * A plus sign will set that method to `true` such as `+required` = `Arg::required(true)`
/// * An exclamation will set that method to `false` such as `!required` = `Arg::required(false)` /// * An exclamation will set that method to `false` such as `!required` = `Arg::required(false)`
/// * A `#{min, max}` will set [`Arg::min_values(min)`] and [`Arg::max_values(max)`] /// * A `#{min, max}` will set [`Arg::min_values(min)`] and [`Arg::max_values(max)`]
/// * An asterisk (`*`) will set `Arg::required(true)` /// * An asterisk (`*`) will set `Arg::required(true)`
/// * Curly brackets around a `fn` will set [`Arg::validator`] as in `{fn}` = `Arg::validator(fn)` /// * Curly brackets around a `fn` will set [`Arg::validator`] as in `{fn}` = `Arg::validator(fn)`
/// * An Arg method that accepts a string followed by square brackets will set that method such as /// * An Arg method that accepts a string followed by square brackets will set that method such as
/// `conflicts_with[FOO]` will set `Arg::conflicts_with("FOO")` (note the lack of quotes around /// `conflicts_with[FOO]` will set `Arg::conflicts_with("FOO")` (note the lack of quotes around
/// `FOO` in the macro) /// `FOO` in the macro)
/// * An Arg method that takes a string and can be set multiple times (such as /// * An Arg method that takes a string and can be set multiple times (such as
/// [`Arg::conflicts_with`]) followed by square brackets and a list of values separated by spaces /// [`Arg::conflicts_with`]) followed by square brackets and a list of values separated by spaces
/// will set that method such as `conflicts_with[FOO BAR BAZ]` will set /// will set that method such as `conflicts_with[FOO BAR BAZ]` will set
/// `Arg::conflicts_with("FOO")`, `Arg::conflicts_with("BAR")`, and `Arg::conflicts_with("BAZ")` /// `Arg::conflicts_with("FOO")`, `Arg::conflicts_with("BAR")`, and `Arg::conflicts_with("BAZ")`
/// (note the lack of quotes around the values in the macro) /// (note the lack of quotes around the values in the macro)
/// ///
/// [`Arg::short`]: ./struct.Arg.html#method.short /// [`Arg::short`]: ./struct.Arg.html#method.short
/// [`Arg::long`]: ./struct.Arg.html#method.long /// [`Arg::long`]: ./struct.Arg.html#method.long
@ -846,3 +846,201 @@ macro_rules! vec_remove_all {
} }
}; };
} }
macro_rules! find_from {
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
let mut ret = None;
for k in $matcher.arg_names() {
if let Some(f) = find_by_name!($_self, &k, flags, iter) {
if let Some(ref v) = f.$from() {
if v.contains($arg_name) {
ret = Some(f.to_string());
}
}
}
if let Some(o) = find_by_name!($_self, &k, opts, iter) {
if let Some(ref v) = o.$from() {
if v.contains(&$arg_name) {
ret = Some(o.to_string());
}
}
}
if let Some(pos) = find_by_name!($_self, &k, positionals, values) {
if let Some(ref v) = pos.$from() {
if v.contains($arg_name) {
ret = Some(pos.b.name.to_owned());
}
}
}
}
ret
}};
}
macro_rules! find_name_from {
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
let mut ret = None;
for k in $matcher.arg_names() {
if let Some(f) = find_by_name!($_self, &k, flags, iter) {
if let Some(ref v) = f.$from() {
if v.contains($arg_name) {
ret = Some(f.b.name);
}
}
}
if let Some(o) = find_by_name!($_self, &k, opts, iter) {
if let Some(ref v) = o.$from() {
if v.contains(&$arg_name) {
ret = Some(o.b.name);
}
}
}
if let Some(pos) = find_by_name!($_self, &k, positionals, values) {
if let Some(ref v) = pos.$from() {
if v.contains($arg_name) {
ret = Some(pos.b.name);
}
}
}
}
ret
}};
}
// Finds an arg by name
macro_rules! find_by_name {
($_self:ident, $name:expr, $what:ident, $how:ident) => {
$_self.$what.$how().find(|o| &o.b.name == $name)
}
}
// Finds an option including if it's aliasesed
macro_rules! find_opt_by_long {
(@os $_self:ident, $long:expr) => {{
_find_by_long!($_self, $long, opts)
}};
($_self:ident, $long:expr) => {{
_find_by_long!($_self, $long, opts)
}};
}
macro_rules! find_flag_by_long {
(@os $_self:ident, $long:expr) => {{
_find_by_long!($_self, $long, flags)
}};
($_self:ident, $long:expr) => {{
_find_by_long!($_self, $long, flags)
}};
}
macro_rules! find_any_by_long {
($_self:ident, $long:expr, $what:ident) => {
_find_flag_by_long!($_self, $long).or(_find_opt_by_long!($_self, $long))
}
}
macro_rules! _find_by_long {
($_self:ident, $long:expr, $what:ident) => {{
$_self.$what
.iter()
.filter(|a| a.s.long.is_some())
.find(|a| {
&&a.s.long.unwrap() == &$long ||
(a.s.aliases.is_some() &&
a.s
.aliases
.as_ref()
.unwrap()
.iter()
.any(|&(alias, _)| &&alias == &$long))
})
}}
}
// Finds an option
macro_rules! find_opt_by_short {
($_self:ident, $short:expr) => {{
_find_by_short!($_self, $short, opts)
}}
}
macro_rules! find_flag_by_short {
($_self:ident, $short:expr) => {{
_find_by_short!($_self, $short, flags)
}}
}
macro_rules! find_any_by_short {
($_self:ident, $short:expr, $what:ident) => {
_find_flag_by_short!($_self, $short).or(_find_opt_by_short!($_self, $short))
}
}
macro_rules! _find_by_short {
($_self:ident, $short:expr, $what:ident) => {{
$_self.$what
.iter()
.filter(|a| a.s.short.is_some())
.find(|a| a.s.short.unwrap() == $short)
}}
}
macro_rules! find_subcmd {
($_self:expr, $sc:expr) => {{
$_self.subcommands
.iter()
.find(|s| {
s.p.meta.name == $sc ||
(s.p.meta.aliases.is_some() &&
s.p
.meta
.aliases
.as_ref()
.unwrap()
.iter()
.any(|&(n, _)| n == $sc))
})
}};
}
macro_rules! shorts {
($_self:ident) => {{
_shorts_longs!($_self, short)
}};
}
macro_rules! longs {
($_self:ident) => {{
_shorts_longs!($_self, long)
}};
}
macro_rules! _shorts_longs {
($_self:ident, $what:ident) => {{
$_self.flags
.iter()
.filter(|f| f.s.$what.is_some())
.map(|f| f.s.$what.as_ref().unwrap())
.chain($_self.opts.iter()
.filter(|o| o.s.$what.is_some())
.map(|o| o.s.$what.as_ref().unwrap()))
}};
}
macro_rules! arg_names {
($_self:ident) => {{
_names!($_self)
}};
}
macro_rules! _names {
($_self:ident) => {{
$_self.flags
.iter()
.map(|f| &*f.b.name)
.chain($_self.opts.iter()
.map(|o| &*o.b.name)
.chain($_self.positionals.values()
.map(|p| &*p.b.name)))
}};
}

View file

@ -1,9 +1,9 @@
extern crate regex; extern crate regex;
extern crate clap; extern crate clap;
use clap::{App, Arg, SubCommand, Shell}; use clap::{App, Arg, SubCommand, Shell};
use regex::Regex; use regex::Regex;
static BASH: &'static str = r#"_myapp() { static BASH: &'static str = r#"_myapp() {
local i cur prev opts cmds local i cur prev opts cmds
COMPREPLY=() COMPREPLY=()
@ -63,7 +63,7 @@ static BASH: &'static str = r#"_myapp() {
return 0 return 0
;; ;;
myapp__test) myapp__test)
opts=" -h -V --case --help --version " opts=" -h -V --help --version --case "
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0 return 0
@ -85,8 +85,8 @@ static BASH: &'static str = r#"_myapp() {
} }
complete -F _myapp -o bashdefault -o default myapp complete -F _myapp -o bashdefault -o default myapp
"#; "#;
static ZSH: &'static str = r#"#compdef myapp static ZSH: &'static str = r#"#compdef myapp
_myapp() { _myapp() {
@ -152,8 +152,8 @@ _myapp__test_commands() {
_describe -t commands 'myapp test commands' commands "$@" _describe -t commands 'myapp test commands' commands "$@"
} }
_myapp "$@""#; _myapp "$@""#;
static FISH: &'static str = r#"function __fish_using_command static FISH: &'static str = r#"function __fish_using_command
set cmd (commandline -opc) set cmd (commandline -opc)
if [ (count $cmd) -eq (count $argv) ] if [ (count $cmd) -eq (count $argv) ]
@ -176,9 +176,9 @@ complete -c myapp -n "__fish_using_command myapp test" -s h -l help -d "Prints h
complete -c myapp -n "__fish_using_command myapp test" -s V -l version -d "Prints version information" complete -c myapp -n "__fish_using_command myapp test" -s V -l version -d "Prints version information"
complete -c myapp -n "__fish_using_command myapp help" -s h -l help -d "Prints help information" complete -c myapp -n "__fish_using_command myapp help" -s h -l help -d "Prints help information"
complete -c myapp -n "__fish_using_command myapp help" -s V -l version -d "Prints version information" complete -c myapp -n "__fish_using_command myapp help" -s V -l version -d "Prints version information"
"#; "#;
#[cfg(not(target_os="windows"))] #[cfg(not(target_os="windows"))]
static POWERSHELL: &'static str = r#" static POWERSHELL: &'static str = r#"
@('myapp', './myapp') | %{ @('myapp', './myapp') | %{
Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock { Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock {
@ -227,9 +227,9 @@ static POWERSHELL: &'static str = r#"
%{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ } %{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ }
} }
} }
"#; "#;
#[cfg(target_os="windows")] #[cfg(target_os="windows")]
static POWERSHELL: &'static str = r#" static POWERSHELL: &'static str = r#"
@('myapp', './myapp', 'myapp.exe', '.\myapp', '.\myapp.exe', './myapp.exe') | %{ @('myapp', './myapp', 'myapp.exe', '.\myapp', '.\myapp.exe', './myapp.exe') | %{
Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock { Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock {
@ -267,9 +267,9 @@ static POWERSHELL: &'static str = r#"
%{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ } %{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ }
} }
} }
"#; "#;
#[cfg(not(target_os="windows"))] #[cfg(not(target_os="windows"))]
static POWERSHELL_WUS: &'static str = r#" static POWERSHELL_WUS: &'static str = r#"
@('my_app', './my_app') | %{ @('my_app', './my_app') | %{
Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock { Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock {
@ -327,9 +327,9 @@ static POWERSHELL_WUS: &'static str = r#"
%{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ } %{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ }
} }
} }
"#; "#;
#[cfg(target_os="windows")] #[cfg(target_os="windows")]
static POWERSHELL_WUS: &'static str = r#" static POWERSHELL_WUS: &'static str = r#"
@('my_app', './my_app', 'my_app.exe', '.\my_app', '.\my_app.exe', './my_app.exe') | %{ @('my_app', './my_app', 'my_app.exe', '.\my_app', '.\my_app.exe', './my_app.exe') | %{
Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock { Register-ArgumentCompleter -Native -CommandName $_ -ScriptBlock {
@ -374,8 +374,8 @@ static POWERSHELL_WUS: &'static str = r#"
%{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ } %{ New-Object System.Management.Automation.CompletionResult $_, $_, 'ParameterValue', $_ }
} }
} }
"#; "#;
static ZSH_WUS: &'static str = r#"#compdef my_app static ZSH_WUS: &'static str = r#"#compdef my_app
_my_app() { _my_app() {
@ -458,8 +458,8 @@ _my_app__test_commands() {
_describe -t commands 'my_app test commands' commands "$@" _describe -t commands 'my_app test commands' commands "$@"
} }
_my_app "$@""#; _my_app "$@""#;
static FISH_WUS: &'static str = r#"function __fish_using_command static FISH_WUS: &'static str = r#"function __fish_using_command
set cmd (commandline -opc) set cmd (commandline -opc)
if [ (count $cmd) -eq (count $argv) ] if [ (count $cmd) -eq (count $argv) ]
@ -486,8 +486,8 @@ complete -c my_app -n "__fish_using_command my_app some_cmd" -s h -l help -d "Pr
complete -c my_app -n "__fish_using_command my_app some_cmd" -s V -l version -d "Prints version information" complete -c my_app -n "__fish_using_command my_app some_cmd" -s V -l version -d "Prints version information"
complete -c my_app -n "__fish_using_command my_app help" -s h -l help -d "Prints help information" complete -c my_app -n "__fish_using_command my_app help" -s h -l help -d "Prints help information"
complete -c my_app -n "__fish_using_command my_app help" -s V -l version -d "Prints version information" complete -c my_app -n "__fish_using_command my_app help" -s V -l version -d "Prints version information"
"#; "#;
static BASH_WUS: &'static str = r#"_my_app() { static BASH_WUS: &'static str = r#"_my_app() {
local i cur prev opts cmds local i cur prev opts cmds
COMPREPLY=() COMPREPLY=()
@ -550,7 +550,7 @@ static BASH_WUS: &'static str = r#"_my_app() {
return 0 return 0
;; ;;
my_app__some_cmd) my_app__some_cmd)
opts=" -h -V --config --help --version " opts=" -h -V --help --version --config "
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0 return 0
@ -569,7 +569,7 @@ static BASH_WUS: &'static str = r#"_my_app() {
return 0 return 0
;; ;;
my_app__test) my_app__test)
opts=" -h -V --case --help --version " opts=" -h -V --help --version --case "
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0 return 0
@ -591,129 +591,125 @@ static BASH_WUS: &'static str = r#"_my_app() {
} }
complete -F _my_app -o bashdefault -o default my_app complete -F _my_app -o bashdefault -o default my_app
"#; "#;
fn compare(left: &str, right: &str) -> bool { fn compare(left: &str, right: &str) -> bool {
let b = left == right; let b = left == right;
if !b { if !b {
let re = Regex::new(" ").unwrap(); let re = Regex::new(" ").unwrap();
println!(""); println!("");
println!("--> left"); println!("--> left");
// println!("{}", left); // println!("{}", left);
println!("{}", re.replace_all(left, "\u{2022}")); println!("{}", re.replace_all(left, "\u{2022}"));
println!("--> right"); println!("--> right");
println!("{}", re.replace_all(right, "\u{2022}")); println!("{}", re.replace_all(right, "\u{2022}"));
// println!("{}", right); // println!("{}", right);
println!("--") println!("--")
} }
b b
} }
fn build_app() -> App<'static, 'static> { fn build_app() -> App<'static, 'static> { build_app_with_name("myapp") }
build_app_with_name("myapp")
} fn build_app_with_name(s: &'static str) -> App<'static, 'static> {
App::new(s)
fn build_app_with_name(s: &'static str) -> App<'static, 'static> { .about("Tests completions")
App::new(s) .arg(Arg::with_name("file").help("some input file"))
.about("Tests completions") .subcommand(SubCommand::with_name("test")
.arg(Arg::with_name("file") .about("tests things")
.help("some input file")) .arg(Arg::with_name("case")
.subcommand(SubCommand::with_name("test") .long("case")
.about("tests things") .takes_value(true)
.arg(Arg::with_name("case") .help("the case to test")))
.long("case") }
.takes_value(true)
.help("the case to test"))) fn build_app_with_underscore() -> App<'static, 'static> {
} build_app_with_name("my_app").subcommand(SubCommand::with_name("some_cmd")
.about("tests other things")
fn build_app_with_underscore() -> App<'static, 'static> { .arg(Arg::with_name("config")
build_app_with_name("my_app") .long("--config")
.subcommand(SubCommand::with_name("some_cmd") .takes_value(true)
.about("tests other things") .help("the other case to test")))
.arg(Arg::with_name("config") }
.long("--config")
.takes_value(true) #[test]
.help("the other case to test"))) fn bash() {
} let mut app = build_app();
let mut buf = vec![];
#[test] app.gen_completions_to("myapp", Shell::Bash, &mut buf);
fn bash() { let string = String::from_utf8(buf).unwrap();
let mut app = build_app();
let mut buf = vec![]; assert!(compare(&*string, BASH));
app.gen_completions_to("myapp", Shell::Bash, &mut buf); }
let string = String::from_utf8(buf).unwrap();
#[test]
assert!(compare(&*string, BASH)); fn zsh() {
} let mut app = build_app();
let mut buf = vec![];
#[test] app.gen_completions_to("myapp", Shell::Zsh, &mut buf);
fn zsh() { let string = String::from_utf8(buf).unwrap();
let mut app = build_app();
let mut buf = vec![]; assert!(compare(&*string, ZSH));
app.gen_completions_to("myapp", Shell::Zsh, &mut buf); }
let string = String::from_utf8(buf).unwrap();
#[test]
assert!(compare(&*string, ZSH)); fn fish() {
} let mut app = build_app();
let mut buf = vec![];
#[test] app.gen_completions_to("myapp", Shell::Fish, &mut buf);
fn fish() { let string = String::from_utf8(buf).unwrap();
let mut app = build_app();
let mut buf = vec![]; assert!(compare(&*string, FISH));
app.gen_completions_to("myapp", Shell::Fish, &mut buf); }
let string = String::from_utf8(buf).unwrap();
// Disabled until I figure out this windows line ending and AppVeyor issues
assert!(compare(&*string, FISH)); //#[test]
} // fn powershell() {
// let mut app = build_app();
// Disabled until I figure out this windows line ending and AppVeyor issues // let mut buf = vec![];
//#[test] // app.gen_completions_to("myapp", Shell::PowerShell, &mut buf);
fn powershell() { // let string = String::from_utf8(buf).unwrap();
let mut app = build_app(); //
let mut buf = vec![]; // assert!(compare(&*string, POWERSHELL));
app.gen_completions_to("myapp", Shell::PowerShell, &mut buf); // }
let string = String::from_utf8(buf).unwrap();
// Disabled until I figure out this windows line ending and AppVeyor issues
assert!(compare(&*string, POWERSHELL)); //#[test]
} // fn powershell_with_underscore() {
// let mut app = build_app_with_underscore();
// Disabled until I figure out this windows line ending and AppVeyor issues // let mut buf = vec![];
//#[test] // app.gen_completions_to("my_app", Shell::PowerShell, &mut buf);
fn powershell_with_underscore() { // let string = String::from_utf8(buf).unwrap();
let mut app = build_app_with_underscore(); //
let mut buf = vec![]; // assert!(compare(&*string, POWERSHELL_WUS));
app.gen_completions_to("my_app", Shell::PowerShell, &mut buf); // }
let string = String::from_utf8(buf).unwrap();
#[test]
assert!(compare(&*string, POWERSHELL_WUS)); fn bash_with_underscore() {
} let mut app = build_app_with_underscore();
let mut buf = vec![];
#[test] app.gen_completions_to("my_app", Shell::Bash, &mut buf);
fn bash_with_underscore() { let string = String::from_utf8(buf).unwrap();
let mut app = build_app_with_underscore();
let mut buf = vec![]; assert!(compare(&*string, BASH_WUS));
app.gen_completions_to("my_app", Shell::Bash, &mut buf); }
let string = String::from_utf8(buf).unwrap();
#[test]
assert!(compare(&*string, BASH_WUS)); fn fish_with_underscore() {
} let mut app = build_app_with_underscore();
let mut buf = vec![];
#[test] app.gen_completions_to("my_app", Shell::Fish, &mut buf);
fn fish_with_underscore() { let string = String::from_utf8(buf).unwrap();
let mut app = build_app_with_underscore();
let mut buf = vec![]; assert!(compare(&*string, FISH_WUS));
app.gen_completions_to("my_app", Shell::Fish, &mut buf); }
let string = String::from_utf8(buf).unwrap();
#[test]
assert!(compare(&*string, FISH_WUS)); fn zsh_with_underscore() {
} let mut app = build_app_with_underscore();
let mut buf = vec![];
#[test] app.gen_completions_to("my_app", Shell::Zsh, &mut buf);
fn zsh_with_underscore() { let string = String::from_utf8(buf).unwrap();
let mut app = build_app_with_underscore();
let mut buf = vec![]; assert!(compare(&*string, ZSH_WUS));
app.gen_completions_to("my_app", Shell::Zsh, &mut buf); }
let string = String::from_utf8(buf).unwrap();
assert!(compare(&*string, ZSH_WUS));
}

View file

@ -278,8 +278,7 @@ fn help_subcommand() {
#[test] #[test]
fn subcommand_short_help() { fn subcommand_short_help() {
let m = test::complex_app() let m = test::complex_app().get_matches_from_safe(vec!["clap-test", "subcmd", "-h"]);
.get_matches_from_safe(vec!["clap-test", "subcmd", "-h"]);
assert!(m.is_err()); assert!(m.is_err());
assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed); assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
@ -287,8 +286,7 @@ fn subcommand_short_help() {
#[test] #[test]
fn subcommand_long_help() { fn subcommand_long_help() {
let m = test::complex_app() let m = test::complex_app().get_matches_from_safe(vec!["clap-test", "subcmd", "--help"]);
.get_matches_from_safe(vec!["clap-test", "subcmd", "--help"]);
assert!(m.is_err()); assert!(m.is_err());
assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed); assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
@ -296,8 +294,7 @@ fn subcommand_long_help() {
#[test] #[test]
fn subcommand_help_rev() { fn subcommand_help_rev() {
let m = test::complex_app() let m = test::complex_app().get_matches_from_safe(vec!["clap-test", "help", "subcmd"]);
.get_matches_from_safe(vec!["clap-test", "help", "subcmd"]);
assert!(m.is_err()); assert!(m.is_err());
assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed); assert_eq!(m.unwrap_err().kind, ErrorKind::HelpDisplayed);
@ -321,12 +318,11 @@ fn after_and_before_help_output() {
#[test] #[test]
fn multi_level_sc_help() { fn multi_level_sc_help() {
let app = App::new("ctest") let app = App::new("ctest")
.subcommand(SubCommand::with_name("subcmd") .subcommand(SubCommand::with_name("subcmd").subcommand(SubCommand::with_name("multi")
.subcommand(SubCommand::with_name("multi") .about("tests subcommands")
.about("tests subcommands") .author("Kevin K. <kbknapp@gmail.com>")
.author("Kevin K. <kbknapp@gmail.com>") .version("0.1")
.version("0.1") .args_from_usage("
.args_from_usage("
-f, --flag 'tests flags' -f, --flag 'tests flags'
-o, --option [scoption]... 'tests options' -o, --option [scoption]... 'tests options'
"))); ")));
@ -354,16 +350,16 @@ fn issue_626_unicode_cutoff() {
.version("0.1") .version("0.1")
.set_term_width(70) .set_term_width(70)
.arg(Arg::with_name("cafe") .arg(Arg::with_name("cafe")
.short("c") .short("c")
.long("cafe") .long("cafe")
.value_name("FILE") .value_name("FILE")
.help("A coffeehouse, coffee shop, or café is an establishment \ .help("A coffeehouse, coffee shop, or café is an establishment \
which primarily serves hot coffee, related coffee beverages \ which primarily serves hot coffee, related coffee beverages \
(e.g., café latte, cappuccino, espresso), tea, and other hot \ (e.g., café latte, cappuccino, espresso), tea, and other hot \
beverages. Some coffeehouses also serve cold beverages such as \ beverages. Some coffeehouses also serve cold beverages such as \
iced coffee and iced tea. Many cafés also serve some type of \ iced coffee and iced tea. Many cafés also serve some type of \
food, such as light snacks, muffins, or pastries.") food, such as light snacks, muffins, or pastries.")
.takes_value(true)); .takes_value(true));
assert!(test::compare_output(app, "ctest --help", ISSUE_626_CUTOFF, false)); assert!(test::compare_output(app, "ctest --help", ISSUE_626_CUTOFF, false));
} }
@ -372,20 +368,20 @@ fn hide_possible_vals() {
let app = App::new("ctest") let app = App::new("ctest")
.version("0.1") .version("0.1")
.arg(Arg::with_name("pos") .arg(Arg::with_name("pos")
.short("p") .short("p")
.long("pos") .long("pos")
.value_name("VAL") .value_name("VAL")
.possible_values(&["fast", "slow"]) .possible_values(&["fast", "slow"])
.help("Some vals") .help("Some vals")
.takes_value(true)) .takes_value(true))
.arg(Arg::with_name("cafe") .arg(Arg::with_name("cafe")
.short("c") .short("c")
.long("cafe") .long("cafe")
.value_name("FILE") .value_name("FILE")
.hide_possible_values(true) .hide_possible_values(true)
.possible_values(&["fast", "slow"]) .possible_values(&["fast", "slow"])
.help("A coffeehouse, coffee shop, or café.") .help("A coffeehouse, coffee shop, or café.")
.takes_value(true)); .takes_value(true));
assert!(test::compare_output(app, "ctest --help", HIDE_POS_VALS, false)); assert!(test::compare_output(app, "ctest --help", HIDE_POS_VALS, false));
} }
@ -441,9 +437,9 @@ fn old_newline_chars() {
#[test] #[test]
fn issue_688_hidden_pos_vals() { fn issue_688_hidden_pos_vals() {
let filter_values = ["Nearest", "Linear", "Cubic", "Gaussian", "Lanczos3"]; let filter_values = ["Nearest", "Linear", "Cubic", "Gaussian", "Lanczos3"];
let app1 = App::new("ctest") let app1 = App::new("ctest")
.version("0.1") .version("0.1")
.set_term_width(120) .set_term_width(120)
.setting(AppSettings::HidePossibleValuesInHelp) .setting(AppSettings::HidePossibleValuesInHelp)
@ -455,7 +451,7 @@ fn issue_688_hidden_pos_vals() {
.takes_value(true)); .takes_value(true));
assert!(test::compare_output(app1, "ctest --help", ISSUE_688, false)); assert!(test::compare_output(app1, "ctest --help", ISSUE_688, false));
let app2 = App::new("ctest") let app2 = App::new("ctest")
.version("0.1") .version("0.1")
.set_term_width(120) .set_term_width(120)
.arg(Arg::with_name("filter") .arg(Arg::with_name("filter")
@ -466,7 +462,7 @@ fn issue_688_hidden_pos_vals() {
.takes_value(true)); .takes_value(true));
assert!(test::compare_output(app2, "ctest --help", ISSUE_688, false)); assert!(test::compare_output(app2, "ctest --help", ISSUE_688, false));
let app3 = App::new("ctest") let app3 = App::new("ctest")
.version("0.1") .version("0.1")
.set_term_width(120) .set_term_width(120)
.arg(Arg::with_name("filter") .arg(Arg::with_name("filter")
@ -483,27 +479,26 @@ fn issue_702_multiple_values() {
.version("1.0") .version("1.0")
.author("foo") .author("foo")
.about("bar") .about("bar")
.arg(Arg::with_name("arg1") .arg(Arg::with_name("arg1").help("some option"))
.help("some option"))
.arg(Arg::with_name("arg2") .arg(Arg::with_name("arg2")
.multiple(true) .multiple(true)
.help("some option")) .help("some option"))
.arg(Arg::with_name("some") .arg(Arg::with_name("some")
.help("some option") .help("some option")
.short("s") .short("s")
.long("some") .long("some")
.takes_value(true)) .takes_value(true))
.arg(Arg::with_name("other") .arg(Arg::with_name("other")
.help("some other option") .help("some other option")
.short("o") .short("o")
.long("other") .long("other")
.takes_value(true)) .takes_value(true))
.arg(Arg::with_name("label") .arg(Arg::with_name("label")
.help("a label") .help("a label")
.short("l") .short("l")
.long("label") .long("label")
.multiple(true) .multiple(true)
.takes_value(true)); .takes_value(true));
assert!(test::compare_output(app, "myapp --help", ISSUE_702, false)); assert!(test::compare_output(app, "myapp --help", ISSUE_702, false));
} }
@ -512,17 +507,17 @@ fn issue_760() {
let app = App::new("ctest") let app = App::new("ctest")
.version("0.1") .version("0.1")
.arg(Arg::with_name("option") .arg(Arg::with_name("option")
.help("tests options") .help("tests options")
.short("o") .short("o")
.long("option") .long("option")
.takes_value(true) .takes_value(true)
.multiple(true) .multiple(true)
.number_of_values(1)) .number_of_values(1))
.arg(Arg::with_name("opt") .arg(Arg::with_name("opt")
.help("tests options") .help("tests options")
.short("O") .short("O")
.long("opt") .long("opt")
.takes_value(true)); .takes_value(true));
assert!(test::compare_output(app, "ctest --help", ISSUE_760, false)); assert!(test::compare_output(app, "ctest --help", ISSUE_760, false));
} }
#[test] #[test]

View file

@ -106,8 +106,7 @@ fn quoted_arg_long_name() {
(@arg scpositional: index(1) "tests positionals")) (@arg scpositional: index(1) "tests positionals"))
); );
assert_eq!(app.p.long_list[2], "long-option-2"); let matches = app.get_matches_from_safe(vec!["bin_name", "value1", "value2", "--long-option-2"])
.expect("Expected to successfully match the given args.");
let matches = app.get_matches_from_safe(vec!["bin_name", "value1", "value2", "--long-option-2"]).expect("Expected to successfully match the given args.");
assert!(matches.is_present("option2")); assert!(matches.is_present("option2"));
} }

View file

@ -18,7 +18,7 @@ static COND_REQ_IN_USAGE: &'static str = "error: The following required argument
--output <output> --output <output>
USAGE: USAGE:
test --target <target> --input <input> --output <output> test --input <input> --output <output> --target <target>
For more information try --help"; For more information try --help";
@ -46,7 +46,7 @@ fn flag_required_2() {
#[test] #[test]
fn option_required() { fn option_required() {
let result = App::new("option_required") let result = App::new("option_required")
.arg(Arg::from_usage("-f [flag] 'some flag'").requires("color")) .arg(Arg::from_usage("-f [flag] 'some flag'").requires("c"))
.arg(Arg::from_usage("-c [color] 'third flag'")) .arg(Arg::from_usage("-c [color] 'third flag'"))
.get_matches_from_safe(vec!["", "-f", "val"]); .get_matches_from_safe(vec!["", "-f", "val"]);
assert!(result.is_err()); assert!(result.is_err());
@ -560,4 +560,4 @@ fn required_ifs_wrong_val_mult_fail() {
assert!(res.is_err()); assert!(res.is_err());
assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument);
} }

View file

@ -2,30 +2,21 @@ extern crate clap;
use clap::{App, Arg}; use clap::{App, Arg};
#[test] #[test]
#[should_panic] #[should_panic]
fn unique_arg_names() { fn unique_arg_names() {
App::new("some").args(&[ App::new("some").args(&[Arg::with_name("arg").short("a"), Arg::with_name("arg").short("b")]);
Arg::with_name("arg").short("a"),
Arg::with_name("arg").short("b")
]);
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn unique_arg_shorts() { fn unique_arg_shorts() {
App::new("some").args(&[ App::new("some").args(&[Arg::with_name("arg1").short("a"), Arg::with_name("arg2").short("a")]);
Arg::with_name("arg1").short("a"),
Arg::with_name("arg2").short("a")
]);
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn unique_arg_longs() { fn unique_arg_longs() {
App::new("some").args(&[ App::new("some")
Arg::with_name("arg1").long("long"), .args(&[Arg::with_name("arg1").long("long"), Arg::with_name("arg2").long("long")]);
Arg::with_name("arg2").long("long")
]);
} }