Auto merge of #800 - kbknapp:issue-782-take2, r=kbknapp

Issue 782 take2
This commit is contained in:
Homu 2017-01-03 05:59:45 +09:00
commit 446ec981d1
12 changed files with 276 additions and 125 deletions

View file

@ -21,7 +21,7 @@ unicode-width = "=0.1.4"
unicode-segmentation = "1.0.1"
strsim = { version = "=0.6.0", optional = true }
ansi_term = { version = "=0.9.0", optional = true }
term_size = { version = "=0.2.0", optional = true }
term_size = { version = "=0.2.1", optional = true }
libc = { version = "=0.2.18", optional = true }
yaml-rust = { version = "=0.3.5", optional = true }
clippy = { version = "~0.0.104", optional = true }

View file

@ -4,7 +4,7 @@
extern crate clap;
extern crate test;
use clap::{App, Arg, SubCommand};
use clap::{App, Arg, SubCommand, AppSettings};
use test::Bencher;
@ -219,6 +219,10 @@ fn parse_complex2(b: &mut Bencher) {
b.iter(|| create_app!().get_matches_from(vec!["myprog", "arg1", "-f", "arg2", "--long-option-2", "some", "-O", "slow", "--multvalsmo", "one", "two", "--minvals2", "3", "2", "1"]));
}
#[bench]
fn parse_complex2_with_args_negate_scs(b: &mut Bencher) {
b.iter(|| create_app!().setting(AppSettings::ArgsNegateSubcommands).get_matches_from(vec!["myprog", "arg1", "-f", "arg2", "--long-option-2", "some", "-O", "slow", "--multvalsmo", "one", "two", "--minvals2", "3", "2", "1"]));
}
#[bench]
fn parse_sc_complex(b: &mut Bencher) {

View file

@ -486,7 +486,7 @@ impl<'a, 'b> App<'a, 'b> {
/// [`AppSettings`]: ./enum.AppSettings.html
pub fn global_setting(mut self, setting: AppSettings) -> Self {
self.p.set(setting);
self.p.g_settings.push(setting);
self.p.g_settings.set(setting);
self
}
@ -510,7 +510,7 @@ impl<'a, 'b> App<'a, 'b> {
pub fn global_settings(mut self, settings: &[AppSettings]) -> Self {
for s in settings {
self.p.set(*s);
self.p.g_settings.push(*s)
self.p.g_settings.set(*s)
}
self
}
@ -1517,6 +1517,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
fn required_unless(&self) -> Option<&[&'e str]> { None }
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
fn is_set(&self, _: ArgSettings) -> bool { false }
fn val_terminator(&self) -> Option<&'e str> {None}
fn set(&mut self, _: ArgSettings) {
unreachable!("App struct does not support AnyArg::set, this is a bug!")
}

View file

@ -57,7 +57,7 @@ pub struct Parser<'a, 'b>
help_short: Option<char>,
version_short: Option<char>,
settings: AppFlags,
pub g_settings: Vec<AppSettings>,
pub g_settings: AppFlags,
pub meta: AppMeta<'b>,
pub id: usize,
trailing_vals: bool,
@ -83,7 +83,7 @@ impl<'a, 'b> Default for Parser<'a, 'b> {
groups: HashMap::new(),
global_args: vec![],
overrides: vec![],
g_settings: vec![],
g_settings: AppFlags::new(),
settings: AppFlags::new(),
meta: AppMeta::new(),
trailing_vals: false,
@ -272,10 +272,8 @@ impl<'a, 'b> Parser<'a, 'b>
sc.p.set(AppSettings::GlobalVersion);
sc.p.meta.version = Some(self.meta.version.unwrap());
}
for s in &self.g_settings {
sc.p.set(*s);
sc.p.g_settings.push(*s);
}
sc.p.settings = sc.p.settings | self.g_settings;
sc.p.g_settings = sc.p.settings | self.g_settings;
}
sc.p.propogate_settings();
}
@ -553,14 +551,17 @@ impl<'a, 'b> Parser<'a, 'b>
&& p.v.num_vals.is_none()).map(|_| 1).sum::<u64>() <= 1,
"Only one positional argument with .multiple(true) set is allowed per command");
debug_assert!(self.positionals.values()
.rev()
.next()
.unwrap()
.is_set(ArgSettings::Required),
debug_assert!({
let mut it = self.positionals.values().rev();
// Either the final positional is required
it.next().unwrap().is_set(ArgSettings::Required)
// Or the second to last has a terminator set
|| it.next().unwrap().v.terminator.is_some()
},
"When using a positional argument with .multiple(true) that is *not the last* \
positional argument, the last positional argument (i.e the one with the highest \
index) *must* have .required(true) set.");
index) *must* have .required(true) set."
);
debug_assert!({
let num = self.positionals.len() - 1;
@ -611,7 +612,7 @@ impl<'a, 'b> Parser<'a, 'b>
// Checks if the arg matches a subcommand name, or any of it's aliases (if defined)
#[inline]
fn possible_subcommand(&self, arg_os: &OsStr) -> bool {
debugln!("Parser::possible_subcommand;");
debugln!("Parser::possible_subcommand: arg={:?}", arg_os);
if self.is_set(AppSettings::ArgsNegateSubcommands) && self.valid_arg {
return false;
}
@ -691,9 +692,7 @@ impl<'a, 'b> Parser<'a, 'b>
pb.b.help = Some("The subcommand whose help message to display");
pb.set(ArgSettings::Multiple);
sc.positionals.insert(1, pb);
for s in self.g_settings.clone() {
sc.set(s);
}
sc.settings = sc.settings | self.g_settings;
} else {
sc.create_help_and_version();
}
@ -704,8 +703,8 @@ impl<'a, 'b> Parser<'a, 'b>
}
#[inline]
fn check_is_new_arg(&mut self, arg_os: &OsStr, needs_val_of: Option<&'a str>) -> bool {
debugln!("Parser::check_is_new_arg: Needs Val of={:?}", needs_val_of);
fn is_new_arg(&mut self, arg_os: &OsStr, needs_val_of: Option<&'a str>) -> bool {
debugln!("Parser::is_new_arg: arg={:?}, Needs Val of={:?}", arg_os, needs_val_of);
let app_wide_settings = if self.is_set(AppSettings::AllowLeadingHyphen) {
true
} else if self.is_set(AppSettings::AllowNegativeNumbers) {
@ -721,19 +720,19 @@ impl<'a, 'b> Parser<'a, 'b>
};
let arg_allows_tac = if let Some(name) = needs_val_of {
if let Some(o) = find_by_name!(self, &name, opts, iter) {
!(o.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings)
} else if let Some(p) = find_by_name!(self, &name, opts, iter) {
!(p.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings)
(o.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings)
} else if let Some(p) = find_by_name!(self, &name, positionals, values) {
(p.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings)
} else {
true
false
}
} else {
true
false
};
debugln!("Parser::check_is_new_arg: Does arg allow leading hyphen...{:?}", !arg_allows_tac);
debugln!("Parser::is_new_arg: Does arg allow leading hyphen...{:?}", arg_allows_tac);
// Is this a new argument, or values from a previous option?
debug!("Parser::check_is_new_arg: Starts new arg...");
debug!("Parser::is_new_arg: Starts new arg...");
let mut ret = if arg_os.starts_with(b"--") {
sdebugln!("--");
if arg_os.len_() == 2 {
@ -749,9 +748,9 @@ impl<'a, 'b> Parser<'a, 'b>
false
};
ret = ret && arg_allows_tac;
ret = ret && !arg_allows_tac;
debugln!("Parser::check_is_new_arg: Starts new arg...{:?}", ret);
debugln!("Parser::is_new_arg: Starts new arg...{:?}", ret);
ret
}
@ -781,94 +780,102 @@ impl<'a, 'b> Parser<'a, 'b>
self.valid_neg_num = false;
// Is this a new argument, or values from a previous option?
let starts_new_arg = self.check_is_new_arg(&arg_os, needs_val_of);
let starts_new_arg = self.is_new_arg(&arg_os, needs_val_of);
// Has the user already passed '--'? Meaning only positional args follow
if !self.trailing_vals {
// Does the arg match a subcommand name, or any of it's aliases (if defined)
let pos_sc = self.possible_subcommand(&arg_os);
// If the arg doesn't start with a `-` (except numbers, or AllowLeadingHyphen) and
// isn't a subcommand
if !starts_new_arg && !pos_sc {
// Check to see if parsing a value from an option
if let Some(name) = needs_val_of {
// get the OptBuilder so we can check the settings
if let Some(arg) = find_by_name!(self, &name, opts, iter) {
needs_val_of = try!(self.add_val_to_arg(&*arg, &arg_os, matcher));
// get the next value from the iterator
continue;
}
}
}
if arg_os.starts_with(b"--") {
if arg_os.len_() == 2 {
// The user has passed '--' which means only positional args follow no
// matter what they start with
self.trailing_vals = true;
continue;
}
needs_val_of = try!(self.parse_long_arg(matcher, &arg_os));
if !(needs_val_of.is_none() && self.is_set(AppSettings::AllowLeadingHyphen)) {
continue;
}
} else if arg_os.starts_with(b"-") && arg_os.len_() != 1 {
// Try to parse short args like normal, if AllowLeadingHyphen or
// AllowNegativeNumbers is set, parse_short_arg will *not* throw
// an error, and instead return Ok(None)
needs_val_of = try!(self.parse_short_arg(matcher, &arg_os));
// If it's None, we then check if one of those two AppSettings was set
if needs_val_of.is_none() {
if self.is_set(AppSettings::AllowNegativeNumbers) {
if !(arg_os.to_string_lossy().parse::<i64>().is_ok() ||
arg_os.to_string_lossy().parse::<f64>().is_ok()) {
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
"",
&*self.create_current_usage(matcher),
self.color()));
}
} else if !self.is_set(AppSettings::AllowLeadingHyphen) {
continue;
}
} else {
continue;
}
}
if pos_sc {
if self.possible_subcommand(&arg_os) {
if &*arg_os == "help" && self.is_set(AppSettings::NeedsSubcommandHelp) {
try!(self.parse_help_subcommand(it));
}
subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned());
break;
} else if self.is_set(AppSettings::ArgsNegateSubcommands) {
();
} else if let Some(cdate) =
}
if !starts_new_arg {
if let Some(name) = needs_val_of {
// Check to see if parsing a value from a previous arg
if let Some(arg) = find_by_name!(self, &name, opts, iter) {
// get the OptBuilder so we can check the settings
needs_val_of = try!(self.add_val_to_arg(&*arg, &arg_os, matcher));
// get the next value from the iterator
continue;
}
}
} else {
if arg_os.starts_with(b"--") {
if arg_os.len_() == 2 {
// The user has passed '--' which means only positional args follow no
// matter what they start with
self.trailing_vals = true;
continue;
}
needs_val_of = try!(self.parse_long_arg(matcher, &arg_os));
if !(needs_val_of.is_none() && self.is_set(AppSettings::AllowLeadingHyphen)) {
continue;
}
} else if arg_os.starts_with(b"-") && arg_os.len_() != 1 {
// Try to parse short args like normal, if AllowLeadingHyphen or
// AllowNegativeNumbers is set, parse_short_arg will *not* throw
// an error, and instead return Ok(None)
needs_val_of = try!(self.parse_short_arg(matcher, &arg_os));
// If it's None, we then check if one of those two AppSettings was set
if needs_val_of.is_none() {
if self.is_set(AppSettings::AllowNegativeNumbers) {
if !(arg_os.to_string_lossy().parse::<i64>().is_ok() ||
arg_os.to_string_lossy().parse::<f64>().is_ok()) {
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
"",
&*self.create_current_usage(matcher),
self.color()));
}
} else if !self.is_set(AppSettings::AllowLeadingHyphen) {
continue;
}
} else {
continue;
}
}
}
if !self.is_set(AppSettings::ArgsNegateSubcommands) {
if let Some(cdate) =
suggestions::did_you_mean(&*arg_os.to_string_lossy(),
self.subcommands
.iter()
.map(|s| &s.p.meta.name)) {
return Err(Error::invalid_subcommand(arg_os.to_string_lossy().into_owned(),
cdate,
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name),
&*self.create_current_usage(matcher),
self.color()));
return Err(Error::invalid_subcommand(arg_os.to_string_lossy().into_owned(),
cdate,
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name),
&*self.create_current_usage(matcher),
self.color()));
}
}
}
let low_index_mults = self.is_set(AppSettings::LowIndexMultiplePositional) &&
!self.positionals.is_empty() &&
pos_counter == (self.positionals.len() - 1);
debugln!("Parser::get_matches_with: Low index multiples...{:?}", low_index_mults);
debugln!("Parser::get_matches_with: Positional counter...{}", pos_counter);
debug!("Parser::get_matches_with: Checking for low index multiples...");
if self.is_set(AppSettings::LowIndexMultiplePositional) &&
!self.positionals.is_empty() &&
pos_counter == (self.positionals.len() - 1) {
sdebugln!("Found");
if low_index_mults {
if let Some(na) = it.peek() {
let n = (*na).clone().into();
if self.check_is_new_arg(&n, needs_val_of) || self.possible_subcommand(&n) ||
needs_val_of = if let None = needs_val_of {
if let Some(p) = self.positionals.get(pos_counter) {
Some(p.b.name)
} else {
None
}
} else {
None
};
if self.is_new_arg(&n, needs_val_of) || self.possible_subcommand(&n) ||
suggestions::did_you_mean(&n.to_string_lossy(),
self.subcommands
.iter()
@ -881,8 +888,6 @@ impl<'a, 'b> Parser<'a, 'b>
debugln!("Parser::get_matches_with: Bumping the positional counter...");
pos_counter += 1;
}
} else {
sdebugln!("None");
}
if let Some(p) = self.positionals.get(pos_counter) {
parse_positional!(self, p, arg_os, pos_counter, matcher);
@ -932,14 +937,15 @@ impl<'a, 'b> Parser<'a, 'b>
let sc_name = &*self.subcommands
.iter()
.filter(|sc| sc.p.meta.aliases.is_some())
.filter(|sc| sc.p
.meta
.aliases
.as_ref()
.expect(INTERNAL_ERROR_MSG)
.iter()
.any(|&(a, _)| &a == &&*pos_sc_name)
)
.filter(|sc| {
sc.p
.meta
.aliases
.as_ref()
.expect(INTERNAL_ERROR_MSG)
.iter()
.any(|&(a, _)| &a == &&*pos_sc_name)
})
.map(|sc| sc.p.meta.name.clone())
.next()
.expect(INTERNAL_ERROR_MSG);
@ -972,18 +978,19 @@ impl<'a, 'b> Parser<'a, 'b>
let mut reqs_validated = false;
if let Some(a) = needs_val_of {
debugln!("Parser::validate: needs_val_of={:?}", a);
let o = find_by_name!(self, &a, opts, iter).expect(INTERNAL_ERROR_MSG);
try!(self.validate_required(matcher));
reqs_validated = true;
let should_err = if let Some(v) = matcher.0.args.get(&*o.b.name) {
v.vals.is_empty() && !(o.v.min_vals.is_some() && o.v.min_vals.unwrap() == 0)
} else {
true
};
if should_err {
return Err(Error::empty_value(o,
&*self.create_current_usage(matcher),
self.color()));
if let Some(o) = find_by_name!(self, &a, opts, iter) {
try!(self.validate_required(matcher));
reqs_validated = true;
let should_err = if let Some(v) = matcher.0.args.get(&*o.b.name) {
v.vals.is_empty() && !(o.v.min_vals.is_some() && o.v.min_vals.unwrap() == 0)
} else {
true
};
if should_err {
return Err(Error::empty_value(o,
&*self.create_current_usage(matcher),
self.color()));
}
}
}
@ -1523,6 +1530,11 @@ impl<'a, 'b> Parser<'a, 'b>
{
debugln!("Parser::add_single_val_to_arg;");
debugln!("Parser::add_single_val_to_arg: adding val...{:?}", v);
if let Some(t) = arg.val_terminator() {
if t == v {
return Ok(None);
}
}
matcher.add_val_to(arg.name(), v);
// Increment or create the group "args"
@ -2169,8 +2181,8 @@ impl<'a, 'b> Clone for Parser<'a, 'b>
overrides: self.overrides.clone(),
help_short: self.help_short,
version_short: self.version_short,
settings: self.settings.clone(),
g_settings: self.g_settings.clone(),
settings: self.settings,
g_settings: self.g_settings,
meta: self.meta.clone(),
trailing_vals: self.trailing_vals,
id: self.id,

View file

@ -1,6 +1,7 @@
// Std
use std::ascii::AsciiExt;
use std::str::FromStr;
use std::ops::BitOr;
bitflags! {
flags Flags: u32 {
@ -40,13 +41,20 @@ bitflags! {
}
#[doc(hidden)]
#[derive(Debug)]
#[derive(Debug, Copy)]
pub struct AppFlags(Flags);
impl Clone for AppFlags {
fn clone(&self) -> Self { AppFlags(self.0) }
}
impl BitOr for AppFlags {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
AppFlags(self.0 | rhs.0)
}
}
impl Default for AppFlags {
fn default() -> Self {
AppFlags(NEEDS_LONG_VERSION | NEEDS_LONG_HELP | NEEDS_SC_HELP | UTF8_NONE | COLOR_AUTO)

View file

@ -38,6 +38,7 @@ pub trait AnyArg<'n, 'e>: std_fmt::Display {
fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e str>, &'e str)>>;
fn longest_filter(&self) -> bool;
fn kind(&self) -> ArgKind;
fn val_terminator(&self) -> Option<&'e str>;
}
pub trait DispOrder {

View file

@ -84,6 +84,8 @@ pub struct Arg<'a, 'b>
pub r_unless: Option<Vec<&'a str>>,
#[doc(hidden)]
pub r_ifs: Option<Vec<(&'a str, &'b str)>>,
#[doc(hidden)]
pub val_terminator: Option<&'b str>,
}
impl<'a, 'b> Default for Arg<'a, 'b> {
@ -113,6 +115,7 @@ impl<'a, 'b> Default for Arg<'a, 'b> {
disp_ord: 999,
r_unless: None,
r_ifs: None,
val_terminator: None,
}
}
}
@ -1819,6 +1822,55 @@ impl<'a, 'b> Arg<'a, 'b> {
}
}
/// Specifies a value that *stops* parsing multiple values of a give argument. By default when
/// one sets [`multiple(true)`] on an argument, clap will continue parsing values for that
/// argument until it reaches another valid argument, or one of the other more specific settings
/// for multiple values is used (such as [`min_values`], [`max_values`] or
/// [`number_of_values`]).
///
/// **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
/// of the vaues
///
/// # Examples
///
/// ```rust
/// # use clap::{App, Arg};
/// Arg::with_name("vals")
/// .takes_value(true)
/// .multiple(true)
/// .value_terminator(";")
/// # ;
/// ```
/// The following example uses two arguments, a sequence of commands, and the location in which
/// to perform them
///
/// ```rust
/// # use clap::{App, Arg};
/// let m = App::new("do")
/// .arg(Arg::with_name("cmds")
/// .multiple(true)
/// .allow_hyphen_values(true)
/// .value_terminator(";"))
/// .arg(Arg::with_name("location"))
/// .get_matches_from(vec!["do", "find", "-type", "f", "-name", "special", ";", "/home/clap"]);
/// let cmds: Vec<_> = m.values_of("cmds").unwrap().collect();
/// assert_eq!(&cmds, &["find", "-type", "f", "-name", "special"]);
/// assert_eq!(m.value_of("location"), Some("/home/clap"));
/// ```
/// [options]: ./struct.Arg.html#method.takes_value
/// [positional arguments]: ./struct.Arg.html#method.index
/// [`multiple(true)`]: ./struct.Arg.html#method.multiple
/// [`min_values`]: ./struct.Arg.html#method.min_values
/// [`number_of_values`]: ./struct.Arg.html#method.number_of_values
/// [`max_values`]: ./struct.Arg.html#method.max_values
pub fn value_terminator(mut self, term: &'b str) -> Self {
self.setb(ArgSettings::TakesValue);
self.val_terminator = Some(term);
self
}
/// Specifies that an argument can be matched to all child [`SubCommand`]s.
///
/// **NOTE:** Global arguments *only* propagate down, **not** up (to parent commands)
@ -3153,6 +3205,7 @@ impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for Arg<'a, 'b> {
disp_ord: a.disp_ord,
r_unless: a.r_unless.clone(),
r_ifs: a.r_ifs.clone(),
val_terminator: a.val_terminator.clone(),
}
}
}
@ -3184,6 +3237,7 @@ impl<'a, 'b> Clone for Arg<'a, 'b> {
disp_ord: self.disp_ord,
r_unless: self.r_unless.clone(),
r_ifs: self.r_ifs.clone(),
val_terminator: self.val_terminator.clone(),
}
}
}

View file

@ -71,6 +71,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
fn long(&self) -> Option<&'e str> { self.s.long }
fn val_delim(&self) -> Option<char> { None }
fn help(&self) -> Option<&'e str> { self.b.help }
fn val_terminator(&self) -> Option<&'e str> {None}
fn default_val(&self) -> Option<&'n str> { None }
fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e str>, &'e str)>> {None}
fn longest_filter(&self) -> bool { self.s.long.is_some() }

View file

@ -105,6 +105,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
fn has_switch(&self) -> bool { true }
fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) }
fn max_vals(&self) -> Option<u64> { self.v.max_vals }
fn val_terminator(&self) -> Option<&'e str> { self.v.terminator }
fn num_vals(&self) -> Option<u64> { self.v.num_vals }
fn possible_vals(&self) -> Option<&[&'e str]> { self.v.possible_vals.as_ref().map(|o| &o[..]) }
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {

View file

@ -109,6 +109,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) }
fn has_switch(&self) -> bool { false }
fn max_vals(&self) -> Option<u64> { self.v.max_vals }
fn val_terminator(&self) -> Option<&'e str> { self.v.terminator }
fn num_vals(&self) -> Option<u64> { self.v.num_vals }
fn possible_vals(&self) -> Option<&[&'e str]> { self.v.possible_vals.as_ref().map(|o| &o[..]) }
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {

View file

@ -20,6 +20,7 @@ pub struct Valued<'a, 'b>
pub val_delim: Option<char>,
pub default_val: Option<&'a str>,
pub default_vals_ifs: Option<VecMap<(&'a str, Option<&'b str>, &'b str)>>,
pub terminator: Option<&'b str>,
}
impl<'n, 'e> Default for Valued<'n, 'e> {
@ -35,6 +36,7 @@ impl<'n, 'e> Default for Valued<'n, 'e> {
val_delim: Some(','),
default_val: None,
default_vals_ifs: None,
terminator: None,
}
}
}
@ -52,6 +54,7 @@ impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Valued<'n, 'e> {
val_delim: a.val_delim,
default_val: a.default_val,
default_vals_ifs: a.default_vals_ifs.clone(),
terminator: a.val_terminator.clone(),
};
if let Some(ref vec) = a.val_names {
if vec.len() > 1 {

View file

@ -1105,3 +1105,68 @@ fn low_index_positional_with_flag() {
assert_eq!(m.value_of("target").unwrap(), "target");
assert!(m.is_present("flg"));
}
#[test]
fn multiple_value_terminator_option() {
let m = App::new("lip")
.arg(Arg::with_name("files")
.short("f")
.value_terminator(";")
.multiple(true))
.arg(Arg::with_name("other"))
.get_matches_from_safe(vec![
"lip",
"-f", "val1", "val2", ";",
"otherval"
]);
assert!(m.is_ok(), "{:?}", m.unwrap_err().kind);
let m = m.unwrap();
assert!(m.is_present("other"));
assert_eq!(m.occurrences_of("other"), 1);
assert!(m.is_present("files"));
assert_eq!(m.values_of("files").unwrap().collect::<Vec<_>>(), ["val1", "val2"]);
assert_eq!(m.value_of("other"), Some("otherval"));
}
#[test]
fn multiple_value_terminator_option_other_arg() {
let m = App::new("lip")
.arg(Arg::with_name("files")
.short("f")
.value_terminator(";")
.multiple(true))
.arg(Arg::with_name("other"))
.arg(Arg::with_name("flag")
.short("-F"))
.get_matches_from_safe(vec![
"lip",
"-f", "val1", "val2",
"-F",
"otherval"
]);
assert!(m.is_ok(), "{:?}", m.unwrap_err().kind);
let m = m.unwrap();
assert!(m.is_present("other"));
assert!(m.is_present("files"));
assert_eq!(m.values_of("files").unwrap().collect::<Vec<_>>(), ["val1", "val2"]);
assert_eq!(m.value_of("other"), Some("otherval"));
assert!(m.is_present("flag"));
}
#[test]
fn multiple_vals_with_hyphen() {
let m = App::new("do")
.arg(Arg::with_name("cmds")
.multiple(true)
.allow_hyphen_values(true)
.value_terminator(";"))
.arg(Arg::with_name("location"))
.get_matches_from(vec!["do", "find", "-type", "f", "-name", "special", ";", "/home/clap"]);
let cmds: Vec<_> = m.values_of("cmds").unwrap().collect();
assert_eq!(&cmds, &["find", "-type", "f", "-name", "special"]);
assert_eq!(m.value_of("location"), Some("/home/clap"));
}