mirror of
https://github.com/clap-rs/clap
synced 2025-03-04 23:37:32 +00:00
Auto merge of #800 - kbknapp:issue-782-take2, r=kbknapp
Issue 782 take2
This commit is contained in:
commit
446ec981d1
12 changed files with 276 additions and 125 deletions
|
@ -21,7 +21,7 @@ unicode-width = "=0.1.4"
|
||||||
unicode-segmentation = "1.0.1"
|
unicode-segmentation = "1.0.1"
|
||||||
strsim = { version = "=0.6.0", optional = true }
|
strsim = { version = "=0.6.0", optional = true }
|
||||||
ansi_term = { version = "=0.9.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 }
|
libc = { version = "=0.2.18", optional = true }
|
||||||
yaml-rust = { version = "=0.3.5", optional = true }
|
yaml-rust = { version = "=0.3.5", optional = true }
|
||||||
clippy = { version = "~0.0.104", optional = true }
|
clippy = { version = "~0.0.104", optional = true }
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
use clap::{App, Arg, SubCommand};
|
use clap::{App, Arg, SubCommand, AppSettings};
|
||||||
|
|
||||||
use test::Bencher;
|
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"]));
|
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]
|
#[bench]
|
||||||
fn parse_sc_complex(b: &mut Bencher) {
|
fn parse_sc_complex(b: &mut Bencher) {
|
||||||
|
|
|
@ -486,7 +486,7 @@ impl<'a, 'b> App<'a, 'b> {
|
||||||
/// [`AppSettings`]: ./enum.AppSettings.html
|
/// [`AppSettings`]: ./enum.AppSettings.html
|
||||||
pub fn global_setting(mut self, setting: AppSettings) -> Self {
|
pub fn global_setting(mut self, setting: AppSettings) -> Self {
|
||||||
self.p.set(setting);
|
self.p.set(setting);
|
||||||
self.p.g_settings.push(setting);
|
self.p.g_settings.set(setting);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,7 +510,7 @@ impl<'a, 'b> App<'a, 'b> {
|
||||||
pub fn global_settings(mut self, settings: &[AppSettings]) -> Self {
|
pub fn global_settings(mut self, settings: &[AppSettings]) -> Self {
|
||||||
for s in settings {
|
for s in settings {
|
||||||
self.p.set(*s);
|
self.p.set(*s);
|
||||||
self.p.g_settings.push(*s)
|
self.p.g_settings.set(*s)
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -1517,6 +1517,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
|
||||||
fn required_unless(&self) -> Option<&[&'e str]> { None }
|
fn required_unless(&self) -> Option<&[&'e str]> { None }
|
||||||
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
|
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
|
||||||
fn is_set(&self, _: ArgSettings) -> bool { false }
|
fn is_set(&self, _: ArgSettings) -> bool { false }
|
||||||
|
fn val_terminator(&self) -> Option<&'e str> {None}
|
||||||
fn set(&mut self, _: ArgSettings) {
|
fn set(&mut self, _: ArgSettings) {
|
||||||
unreachable!("App struct does not support AnyArg::set, this is a bug!")
|
unreachable!("App struct does not support AnyArg::set, this is a bug!")
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ pub struct Parser<'a, 'b>
|
||||||
help_short: Option<char>,
|
help_short: Option<char>,
|
||||||
version_short: Option<char>,
|
version_short: Option<char>,
|
||||||
settings: AppFlags,
|
settings: AppFlags,
|
||||||
pub g_settings: Vec<AppSettings>,
|
pub g_settings: AppFlags,
|
||||||
pub meta: AppMeta<'b>,
|
pub meta: AppMeta<'b>,
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
trailing_vals: bool,
|
trailing_vals: bool,
|
||||||
|
@ -83,7 +83,7 @@ impl<'a, 'b> Default for Parser<'a, 'b> {
|
||||||
groups: HashMap::new(),
|
groups: HashMap::new(),
|
||||||
global_args: vec![],
|
global_args: vec![],
|
||||||
overrides: vec![],
|
overrides: vec![],
|
||||||
g_settings: vec![],
|
g_settings: AppFlags::new(),
|
||||||
settings: AppFlags::new(),
|
settings: AppFlags::new(),
|
||||||
meta: AppMeta::new(),
|
meta: AppMeta::new(),
|
||||||
trailing_vals: false,
|
trailing_vals: false,
|
||||||
|
@ -272,10 +272,8 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
sc.p.set(AppSettings::GlobalVersion);
|
sc.p.set(AppSettings::GlobalVersion);
|
||||||
sc.p.meta.version = Some(self.meta.version.unwrap());
|
sc.p.meta.version = Some(self.meta.version.unwrap());
|
||||||
}
|
}
|
||||||
for s in &self.g_settings {
|
sc.p.settings = sc.p.settings | self.g_settings;
|
||||||
sc.p.set(*s);
|
sc.p.g_settings = sc.p.settings | self.g_settings;
|
||||||
sc.p.g_settings.push(*s);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sc.p.propogate_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,
|
&& p.v.num_vals.is_none()).map(|_| 1).sum::<u64>() <= 1,
|
||||||
"Only one positional argument with .multiple(true) set is allowed per command");
|
"Only one positional argument with .multiple(true) set is allowed per command");
|
||||||
|
|
||||||
debug_assert!(self.positionals.values()
|
debug_assert!({
|
||||||
.rev()
|
let mut it = self.positionals.values().rev();
|
||||||
.next()
|
// Either the final positional is required
|
||||||
.unwrap()
|
it.next().unwrap().is_set(ArgSettings::Required)
|
||||||
.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* \
|
"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 \
|
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!({
|
debug_assert!({
|
||||||
let num = self.positionals.len() - 1;
|
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)
|
// Checks if the arg matches a subcommand name, or any of it's aliases (if defined)
|
||||||
#[inline]
|
#[inline]
|
||||||
fn possible_subcommand(&self, arg_os: &OsStr) -> bool {
|
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 {
|
if self.is_set(AppSettings::ArgsNegateSubcommands) && self.valid_arg {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -691,9 +692,7 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
pb.b.help = Some("The subcommand whose help message to display");
|
pb.b.help = Some("The subcommand whose help message to display");
|
||||||
pb.set(ArgSettings::Multiple);
|
pb.set(ArgSettings::Multiple);
|
||||||
sc.positionals.insert(1, pb);
|
sc.positionals.insert(1, pb);
|
||||||
for s in self.g_settings.clone() {
|
sc.settings = sc.settings | self.g_settings;
|
||||||
sc.set(s);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
sc.create_help_and_version();
|
sc.create_help_and_version();
|
||||||
}
|
}
|
||||||
|
@ -704,8 +703,8 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn check_is_new_arg(&mut self, arg_os: &OsStr, needs_val_of: Option<&'a str>) -> bool {
|
fn 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);
|
debugln!("Parser::is_new_arg: arg={:?}, Needs Val of={:?}", arg_os, needs_val_of);
|
||||||
let app_wide_settings = if self.is_set(AppSettings::AllowLeadingHyphen) {
|
let app_wide_settings = if self.is_set(AppSettings::AllowLeadingHyphen) {
|
||||||
true
|
true
|
||||||
} else if self.is_set(AppSettings::AllowNegativeNumbers) {
|
} 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 {
|
let arg_allows_tac = if let Some(name) = needs_val_of {
|
||||||
if let Some(o) = find_by_name!(self, &name, opts, iter) {
|
if let Some(o) = find_by_name!(self, &name, opts, iter) {
|
||||||
!(o.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, opts, iter) {
|
} else if let Some(p) = find_by_name!(self, &name, positionals, values) {
|
||||||
!(p.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings)
|
(p.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings)
|
||||||
} else {
|
} else {
|
||||||
true
|
false
|
||||||
}
|
}
|
||||||
} else {
|
} 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?
|
// 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"--") {
|
let mut ret = if arg_os.starts_with(b"--") {
|
||||||
sdebugln!("--");
|
sdebugln!("--");
|
||||||
if arg_os.len_() == 2 {
|
if arg_os.len_() == 2 {
|
||||||
|
@ -749,9 +748,9 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
false
|
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
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,94 +780,102 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
|
|
||||||
self.valid_neg_num = false;
|
self.valid_neg_num = false;
|
||||||
// Is this a new argument, or values from a previous option?
|
// 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
|
// Has the user already passed '--'? Meaning only positional args follow
|
||||||
if !self.trailing_vals {
|
if !self.trailing_vals {
|
||||||
// Does the arg match a subcommand name, or any of it's aliases (if defined)
|
// Does the arg match a subcommand name, or any of it's aliases (if defined)
|
||||||
let pos_sc = self.possible_subcommand(&arg_os);
|
if 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 &*arg_os == "help" && self.is_set(AppSettings::NeedsSubcommandHelp) {
|
if &*arg_os == "help" && self.is_set(AppSettings::NeedsSubcommandHelp) {
|
||||||
try!(self.parse_help_subcommand(it));
|
try!(self.parse_help_subcommand(it));
|
||||||
}
|
}
|
||||||
subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned());
|
subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned());
|
||||||
break;
|
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(),
|
suggestions::did_you_mean(&*arg_os.to_string_lossy(),
|
||||||
self.subcommands
|
self.subcommands
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| &s.p.meta.name)) {
|
.map(|s| &s.p.meta.name)) {
|
||||||
return Err(Error::invalid_subcommand(arg_os.to_string_lossy().into_owned(),
|
return Err(Error::invalid_subcommand(arg_os.to_string_lossy().into_owned(),
|
||||||
cdate,
|
cdate,
|
||||||
self.meta
|
self.meta
|
||||||
.bin_name
|
.bin_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or(&self.meta.name),
|
.unwrap_or(&self.meta.name),
|
||||||
&*self.create_current_usage(matcher),
|
&*self.create_current_usage(matcher),
|
||||||
self.color()));
|
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);
|
debugln!("Parser::get_matches_with: Positional counter...{}", pos_counter);
|
||||||
debug!("Parser::get_matches_with: Checking for low index multiples...");
|
if low_index_mults {
|
||||||
if self.is_set(AppSettings::LowIndexMultiplePositional) &&
|
|
||||||
!self.positionals.is_empty() &&
|
|
||||||
pos_counter == (self.positionals.len() - 1) {
|
|
||||||
sdebugln!("Found");
|
|
||||||
if let Some(na) = it.peek() {
|
if let Some(na) = it.peek() {
|
||||||
let n = (*na).clone().into();
|
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(),
|
suggestions::did_you_mean(&n.to_string_lossy(),
|
||||||
self.subcommands
|
self.subcommands
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -881,8 +888,6 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
debugln!("Parser::get_matches_with: Bumping the positional counter...");
|
debugln!("Parser::get_matches_with: Bumping the positional counter...");
|
||||||
pos_counter += 1;
|
pos_counter += 1;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
sdebugln!("None");
|
|
||||||
}
|
}
|
||||||
if let Some(p) = self.positionals.get(pos_counter) {
|
if let Some(p) = self.positionals.get(pos_counter) {
|
||||||
parse_positional!(self, p, arg_os, pos_counter, matcher);
|
parse_positional!(self, p, arg_os, pos_counter, matcher);
|
||||||
|
@ -932,14 +937,15 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
let sc_name = &*self.subcommands
|
let sc_name = &*self.subcommands
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|sc| sc.p.meta.aliases.is_some())
|
.filter(|sc| sc.p.meta.aliases.is_some())
|
||||||
.filter(|sc| sc.p
|
.filter(|sc| {
|
||||||
.meta
|
sc.p
|
||||||
.aliases
|
.meta
|
||||||
.as_ref()
|
.aliases
|
||||||
.expect(INTERNAL_ERROR_MSG)
|
.as_ref()
|
||||||
.iter()
|
.expect(INTERNAL_ERROR_MSG)
|
||||||
.any(|&(a, _)| &a == &&*pos_sc_name)
|
.iter()
|
||||||
)
|
.any(|&(a, _)| &a == &&*pos_sc_name)
|
||||||
|
})
|
||||||
.map(|sc| sc.p.meta.name.clone())
|
.map(|sc| sc.p.meta.name.clone())
|
||||||
.next()
|
.next()
|
||||||
.expect(INTERNAL_ERROR_MSG);
|
.expect(INTERNAL_ERROR_MSG);
|
||||||
|
@ -972,18 +978,19 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
let mut reqs_validated = false;
|
let mut reqs_validated = false;
|
||||||
if let Some(a) = needs_val_of {
|
if let Some(a) = needs_val_of {
|
||||||
debugln!("Parser::validate: needs_val_of={:?}", a);
|
debugln!("Parser::validate: needs_val_of={:?}", a);
|
||||||
let o = find_by_name!(self, &a, opts, iter).expect(INTERNAL_ERROR_MSG);
|
if let Some(o) = find_by_name!(self, &a, opts, iter) {
|
||||||
try!(self.validate_required(matcher));
|
try!(self.validate_required(matcher));
|
||||||
reqs_validated = true;
|
reqs_validated = true;
|
||||||
let should_err = if let Some(v) = matcher.0.args.get(&*o.b.name) {
|
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)
|
v.vals.is_empty() && !(o.v.min_vals.is_some() && o.v.min_vals.unwrap() == 0)
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
if should_err {
|
if should_err {
|
||||||
return Err(Error::empty_value(o,
|
return Err(Error::empty_value(o,
|
||||||
&*self.create_current_usage(matcher),
|
&*self.create_current_usage(matcher),
|
||||||
self.color()));
|
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;");
|
||||||
debugln!("Parser::add_single_val_to_arg: adding val...{:?}", v);
|
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);
|
matcher.add_val_to(arg.name(), v);
|
||||||
|
|
||||||
// Increment or create the group "args"
|
// Increment or create the group "args"
|
||||||
|
@ -2169,8 +2181,8 @@ impl<'a, 'b> Clone for Parser<'a, 'b>
|
||||||
overrides: self.overrides.clone(),
|
overrides: self.overrides.clone(),
|
||||||
help_short: self.help_short,
|
help_short: self.help_short,
|
||||||
version_short: self.version_short,
|
version_short: self.version_short,
|
||||||
settings: self.settings.clone(),
|
settings: self.settings,
|
||||||
g_settings: self.g_settings.clone(),
|
g_settings: self.g_settings,
|
||||||
meta: self.meta.clone(),
|
meta: self.meta.clone(),
|
||||||
trailing_vals: self.trailing_vals,
|
trailing_vals: self.trailing_vals,
|
||||||
id: self.id,
|
id: self.id,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Std
|
// Std
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::ops::BitOr;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
flags Flags: u32 {
|
flags Flags: u32 {
|
||||||
|
@ -40,13 +41,20 @@ bitflags! {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy)]
|
||||||
pub struct AppFlags(Flags);
|
pub struct AppFlags(Flags);
|
||||||
|
|
||||||
impl Clone for AppFlags {
|
impl Clone for AppFlags {
|
||||||
fn clone(&self) -> Self { AppFlags(self.0) }
|
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 {
|
impl Default for AppFlags {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
AppFlags(NEEDS_LONG_VERSION | NEEDS_LONG_HELP | NEEDS_SC_HELP | UTF8_NONE | COLOR_AUTO)
|
AppFlags(NEEDS_LONG_VERSION | NEEDS_LONG_HELP | NEEDS_SC_HELP | UTF8_NONE | COLOR_AUTO)
|
||||||
|
|
|
@ -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 default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e str>, &'e str)>>;
|
||||||
fn longest_filter(&self) -> bool;
|
fn longest_filter(&self) -> bool;
|
||||||
fn kind(&self) -> ArgKind;
|
fn kind(&self) -> ArgKind;
|
||||||
|
fn val_terminator(&self) -> Option<&'e str>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DispOrder {
|
pub trait DispOrder {
|
||||||
|
|
|
@ -84,6 +84,8 @@ pub struct Arg<'a, 'b>
|
||||||
pub r_unless: Option<Vec<&'a str>>,
|
pub r_unless: Option<Vec<&'a str>>,
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub r_ifs: Option<Vec<(&'a str, &'b str)>>,
|
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> {
|
impl<'a, 'b> Default for Arg<'a, 'b> {
|
||||||
|
@ -113,6 +115,7 @@ impl<'a, 'b> Default for Arg<'a, 'b> {
|
||||||
disp_ord: 999,
|
disp_ord: 999,
|
||||||
r_unless: None,
|
r_unless: None,
|
||||||
r_ifs: 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.
|
/// Specifies that an argument can be matched to all child [`SubCommand`]s.
|
||||||
///
|
///
|
||||||
/// **NOTE:** Global arguments *only* propagate down, **not** up (to parent commands)
|
/// **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,
|
disp_ord: a.disp_ord,
|
||||||
r_unless: a.r_unless.clone(),
|
r_unless: a.r_unless.clone(),
|
||||||
r_ifs: a.r_ifs.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,
|
disp_ord: self.disp_ord,
|
||||||
r_unless: self.r_unless.clone(),
|
r_unless: self.r_unless.clone(),
|
||||||
r_ifs: self.r_ifs.clone(),
|
r_ifs: self.r_ifs.clone(),
|
||||||
|
val_terminator: self.val_terminator.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
|
||||||
fn long(&self) -> Option<&'e str> { self.s.long }
|
fn long(&self) -> Option<&'e str> { self.s.long }
|
||||||
fn val_delim(&self) -> Option<char> { None }
|
fn val_delim(&self) -> Option<char> { None }
|
||||||
fn help(&self) -> Option<&'e str> { self.b.help }
|
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_val(&self) -> Option<&'n str> { None }
|
||||||
fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e str>, &'e 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() }
|
fn longest_filter(&self) -> bool { self.s.long.is_some() }
|
||||||
|
|
|
@ -105,6 +105,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
|
||||||
fn has_switch(&self) -> bool { true }
|
fn has_switch(&self) -> bool { true }
|
||||||
fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) }
|
fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) }
|
||||||
fn max_vals(&self) -> Option<u64> { self.v.max_vals }
|
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 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 possible_vals(&self) -> Option<&[&'e str]> { self.v.possible_vals.as_ref().map(|o| &o[..]) }
|
||||||
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
||||||
|
|
|
@ -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 set(&mut self, s: ArgSettings) { self.b.settings.set(s) }
|
||||||
fn has_switch(&self) -> bool { false }
|
fn has_switch(&self) -> bool { false }
|
||||||
fn max_vals(&self) -> Option<u64> { self.v.max_vals }
|
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 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 possible_vals(&self) -> Option<&[&'e str]> { self.v.possible_vals.as_ref().map(|o| &o[..]) }
|
||||||
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub struct Valued<'a, 'b>
|
||||||
pub val_delim: Option<char>,
|
pub val_delim: Option<char>,
|
||||||
pub default_val: Option<&'a str>,
|
pub default_val: Option<&'a str>,
|
||||||
pub default_vals_ifs: Option<VecMap<(&'a str, Option<&'b str>, &'b 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> {
|
impl<'n, 'e> Default for Valued<'n, 'e> {
|
||||||
|
@ -35,6 +36,7 @@ impl<'n, 'e> Default for Valued<'n, 'e> {
|
||||||
val_delim: Some(','),
|
val_delim: Some(','),
|
||||||
default_val: None,
|
default_val: None,
|
||||||
default_vals_ifs: 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,
|
val_delim: a.val_delim,
|
||||||
default_val: a.default_val,
|
default_val: a.default_val,
|
||||||
default_vals_ifs: a.default_vals_ifs.clone(),
|
default_vals_ifs: a.default_vals_ifs.clone(),
|
||||||
|
terminator: a.val_terminator.clone(),
|
||||||
};
|
};
|
||||||
if let Some(ref vec) = a.val_names {
|
if let Some(ref vec) = a.val_names {
|
||||||
if vec.len() > 1 {
|
if vec.len() > 1 {
|
||||||
|
|
|
@ -1105,3 +1105,68 @@ fn low_index_positional_with_flag() {
|
||||||
assert_eq!(m.value_of("target").unwrap(), "target");
|
assert_eq!(m.value_of("target").unwrap(), "target");
|
||||||
assert!(m.is_present("flg"));
|
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"));
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue