feat: allows specifying AllowLeadingHyphen style values, but only for specific args vice command wide

One can now use `Arg::allow_hyphen_values(true)` which will enable `--opt -val` style values only
for the specific arg and not command wide.

Closes #742
This commit is contained in:
Kevin K 2016-11-19 18:52:11 -05:00
parent cf9d6ce5cd
commit c0d70febad
No known key found for this signature in database
GPG key ID: 17218E4B3692F01A
10 changed files with 74 additions and 109 deletions

View file

@ -11,6 +11,9 @@
run-test TEST:
cargo test --test {{TEST}}
debug TEST:
cargo test --test {{TEST}} --features debug
run-tests:
cargo test --features "yaml unstable"

View file

@ -142,9 +142,7 @@ macro_rules! parse_positional {
$pos_counter == $_self.positionals.len()) {
$_self.trailing_vals = true;
}
if let Err(e) = $_self.add_val_to_arg($p, &$arg_os, $matcher) {
return Err(e);
}
try!($_self.add_val_to_arg($p, &$arg_os, $matcher));
$matcher.inc_occurrence_of($p.b.name);
let _ = $_self.groups_for_arg($p.b.name)
@ -217,7 +215,7 @@ macro_rules! find_name_from {
}};
}
// Finds an option by name
// 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)

View file

@ -79,19 +79,13 @@ impl<'a, 'b> App<'a, 'b> {
/// let prog = App::new("My Program")
/// # ;
/// ```
pub fn new<S: Into<String>>(n: S) -> Self {
App { p: Parser::with_name(n.into()) }
}
pub fn new<S: Into<String>>(n: S) -> Self { App { p: Parser::with_name(n.into()) } }
/// Get the name of the app
pub fn get_name(&self) -> &str {
&self.p.meta.name
}
pub fn get_name(&self) -> &str { &self.p.meta.name }
/// Get the name of the binary
pub fn get_bin_name(&self) -> Option<&str> {
self.p.meta.bin_name.as_ref().map(|s| s.as_str())
}
pub fn get_bin_name(&self) -> Option<&str> { self.p.meta.bin_name.as_ref().map(|s| s.as_str()) }
/// Creates a new instance of an application requiring a name, but uses the [`crate_authors!`]
/// and [`crate_version!`] macros to fill in the [`App::author`] and [`App::version`] fields.
@ -155,9 +149,7 @@ impl<'a, 'b> App<'a, 'b> {
/// [`examples/17_yaml.yml`]: https://github.com/kbknapp/clap-rs/blob/master/examples/17_yaml.yml
/// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html
#[cfg(feature = "yaml")]
pub fn from_yaml(yaml: &'a Yaml) -> App<'a, 'a> {
App::from(yaml)
}
pub fn from_yaml(yaml: &'a Yaml) -> App<'a, 'a> { App::from(yaml) }
/// Sets a string of author(s) that will be displayed to the user when they
/// request the help information with `--help` or `-h`.
@ -1193,9 +1185,7 @@ impl<'a, 'b> App<'a, 'b> {
/// .get_matches();
/// ```
/// [`env::args_os`]: https://doc.rust-lang.org/std/env/fn.args_os.html
pub fn get_matches(self) -> ArgMatches<'a> {
self.get_matches_from(&mut env::args_os())
}
pub fn get_matches(self) -> ArgMatches<'a> { self.get_matches_from(&mut env::args_os()) }
/// Starts the parsing process. This method will return a [`clap::Result`] type instead of exiting
/// the process on failed parse. By default this method gets matches from [`env::args_os`]
@ -1508,78 +1498,37 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> {
}
impl<'a, 'b> Clone for App<'a, 'b> {
fn clone(&self) -> Self {
App { p: self.p.clone() }
}
fn clone(&self) -> Self { App { p: self.p.clone() } }
}
impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
fn name(&self) -> &'n str {
unreachable!("App struct does not support AnyArg::name, this is a bug!")
}
fn kind(&self) -> ArgKind {
ArgKind::Subcmd
}
fn overrides(&self) -> Option<&[&'e str]> {
None
}
fn requires(&self) -> Option<&[&'e str]> {
None
}
fn blacklist(&self) -> Option<&[&'e str]> {
None
}
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 id(&self) -> usize { self.p.id }
fn kind(&self) -> ArgKind { ArgKind::Subcmd }
fn overrides(&self) -> Option<&[&'e str]> { None }
fn requires(&self) -> Option<&[&'e str]> { None }
fn blacklist(&self) -> Option<&[&'e str]> { None }
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 set(&mut self, _: ArgSettings) {
unreachable!("App struct does not support AnyArg::set, this is a bug!")
}
fn has_switch(&self) -> bool {
false
}
fn max_vals(&self) -> Option<u64> {
None
}
fn num_vals(&self) -> Option<u64> {
None
}
fn possible_vals(&self) -> Option<&[&'e str]> {
None
}
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> {
None
}
fn min_vals(&self) -> Option<u64> {
None
}
fn short(&self) -> Option<char> {
None
}
fn long(&self) -> Option<&'e str> {
None
}
fn val_delim(&self) -> Option<char> {
None
}
fn takes_value(&self) -> bool {
true
}
fn help(&self) -> Option<&'e str> {
self.p.meta.about
}
fn default_val(&self) -> Option<&'n str> {
None
}
fn longest_filter(&self) -> bool {
true
}
fn has_switch(&self) -> bool { false }
fn max_vals(&self) -> Option<u64> { None }
fn num_vals(&self) -> Option<u64> { None }
fn possible_vals(&self) -> Option<&[&'e str]> { None }
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None }
fn min_vals(&self) -> Option<u64> { None }
fn short(&self) -> Option<char> { None }
fn long(&self) -> Option<&'e str> { None }
fn val_delim(&self) -> Option<char> { None }
fn takes_value(&self) -> bool { true }
fn help(&self) -> Option<&'e str> { self.p.meta.about }
fn default_val(&self) -> Option<&'n str> { None }
fn longest_filter(&self) -> bool { true }
fn aliases(&self) -> Option<Vec<&'e str>> {
if let Some(ref aliases) = self.p.meta.aliases {
let vis_aliases: Vec<_> =
@ -1596,7 +1545,5 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
}
impl<'n, 'e> fmt::Display for App<'n, 'e> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.p.meta.name)
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.p.meta.name) }
}

View file

@ -12,6 +12,7 @@ use args::ArgKind;
#[doc(hidden)]
pub trait AnyArg<'n, 'e>: std_fmt::Display {
fn name(&self) -> &'n str;
fn id(&self) -> usize;
fn overrides(&self) -> Option<&[&'e str]>;
fn aliases(&self) -> Option<Vec<&'e str>>;
fn requires(&self) -> Option<&[&'e str]>;

View file

@ -639,6 +639,13 @@ impl<'a, 'b> Arg<'a, 'b> {
/// Allows values which start with a leading hyphen (`-`)
///
/// **WARNING**: When building your CLIs, consider the effects of allowing leading hyphens and
/// the user passing in a value that matches a valid short. For example `prog -opt -F` where
/// `-F` is supposed to be a value, yet `-F` is *also* a valid short for anther arg. Care should
/// should be taken when designing these args. This is compounded by the ability to "stack"
/// short args. I.e. if `-val` is supposed to be a value, but `-v`, `-a`, and `-l` are all valid
/// shorts.
///
/// # Examples
///
/// ```rust

View file

@ -49,6 +49,7 @@ impl<'n, 'e> Display for FlagBuilder<'n, 'e> {
impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
fn name(&self) -> &'n str { self.b.name }
fn id(&self) -> usize { self.b.id }
fn kind(&self) -> ArgKind { ArgKind::Flag }
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
fn requires(&self) -> Option<&[&'e str]> { self.b.requires.as_ref().map(|o| &o[..]) }
@ -98,13 +99,13 @@ mod test {
#[test]
fn flagbuilder_display() {
let mut f = FlagBuilder::new("flg");
f.settings.set(ArgSettings::Multiple);
f.long = Some("flag");
f.b.settings.set(ArgSettings::Multiple);
f.s.long = Some("flag");
assert_eq!(&*format!("{}", f), "--flag");
let mut f2 = FlagBuilder::new("flg");
f2.short = Some('f');
f2.s.short = Some('f');
assert_eq!(&*format!("{}", f2), "-f");
}
@ -112,8 +113,8 @@ mod test {
#[test]
fn flagbuilder_display_single_alias() {
let mut f = FlagBuilder::new("flg");
f.long = Some("flag");
f.aliases = Some(vec![("als", true)]);
f.s.long = Some("flag");
f.s.aliases = Some(vec![("als", true)]);
assert_eq!(&*format!("{}", f), "--flag");
}
@ -121,8 +122,8 @@ mod test {
#[test]
fn flagbuilder_display_multiple_aliases() {
let mut f = FlagBuilder::new("flg");
f.short = Some('f');
f.aliases =
f.s.short = Some('f');
f.s.aliases =
Some(vec![("alias_not_visible", false), ("f2", true), ("f3", true), ("f4", true)]);
assert_eq!(&*format!("{}", f), "-f");
}

View file

@ -88,6 +88,7 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
fn name(&self) -> &'n str { self.b.name }
fn id(&self) -> usize { self.b.id }
fn kind(&self) -> ArgKind { ArgKind::Opt }
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
fn requires(&self) -> Option<&[&'e str]> { self.b.requires.as_ref().map(|o| &o[..]) }
@ -140,8 +141,8 @@ mod test {
#[test]
fn optbuilder_display1() {
let mut o = OptBuilder::new("opt");
o.long = Some("option");
o.settings.set(ArgSettings::Multiple);
o.s.long = Some("option");
o.b.settings.set(ArgSettings::Multiple);
assert_eq!(&*format!("{}", o), "--option <opt>...");
}
@ -153,8 +154,8 @@ mod test {
v_names.insert(1, "name");
let mut o2 = OptBuilder::new("opt");
o2.short = Some('o');
o2.val_names = Some(v_names);
o2.s.short = Some('o');
o2.v.val_names = Some(v_names);
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
}
@ -166,9 +167,9 @@ mod test {
v_names.insert(1, "name");
let mut o2 = OptBuilder::new("opt");
o2.short = Some('o');
o2.val_names = Some(v_names);
o2.settings.set(ArgSettings::Multiple);
o2.s.short = Some('o');
o2.v.val_names = Some(v_names);
o2.b.settings.set(ArgSettings::Multiple);
assert_eq!(&*format!("{}", o2), "-o <file> <name>");
}
@ -176,8 +177,8 @@ mod test {
#[test]
fn optbuilder_display_single_alias() {
let mut o = OptBuilder::new("opt");
o.long = Some("option");
o.aliases = Some(vec![("als", true)]);
o.s.long = Some("option");
o.s.aliases = Some(vec![("als", true)]);
assert_eq!(&*format!("{}", o), "--option <opt>");
}
@ -185,8 +186,8 @@ mod test {
#[test]
fn optbuilder_display_multiple_aliases() {
let mut o = OptBuilder::new("opt");
o.long = Some("option");
o.aliases =
o.s.long = Some("option");
o.s.aliases =
Some(vec![("als_not_visible", false), ("als2", true), ("als3", true), ("als4", true)]);
assert_eq!(&*format!("{}", o), "--option <opt>");
}

View file

@ -95,6 +95,7 @@ impl<'n, 'e> Display for PosBuilder<'n, 'e> {
impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
fn name(&self) -> &'n str { self.b.name }
fn id(&self) -> usize { self.b.id }
fn kind(&self) -> ArgKind { ArgKind::Pos }
fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) }
fn requires(&self) -> Option<&[&'e str]> { self.b.requires.as_ref().map(|o| &o[..]) }
@ -134,7 +135,7 @@ mod test {
#[test]
fn display_mult() {
let mut p = PosBuilder::new("pos", 1);
p.settings.set(ArgSettings::Multiple);
p.b.settings.set(ArgSettings::Multiple);
assert_eq!(&*format!("{}", p), "<pos>...");
}
@ -142,7 +143,7 @@ mod test {
#[test]
fn display_required() {
let mut p2 = PosBuilder::new("pos", 1);
p2.settings.set(ArgSettings::Required);
p2.b.settings.set(ArgSettings::Required);
assert_eq!(&*format!("{}", p2), "<pos>");
}
@ -153,7 +154,7 @@ mod test {
let mut vm = VecMap::new();
vm.insert(0, "file1");
vm.insert(1, "file2");
p2.val_names = Some(vm);
p2.v.val_names = Some(vm);
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
}
@ -161,11 +162,11 @@ mod test {
#[test]
fn display_val_names_req() {
let mut p2 = PosBuilder::new("pos", 1);
p2.settings.set(ArgSettings::Required);
p2.b.settings.set(ArgSettings::Required);
let mut vm = VecMap::new();
vm.insert(0, "file1");
vm.insert(1, "file2");
p2.val_names = Some(vm);
p2.v.val_names = Some(vm);
assert_eq!(&*format!("{}", p2), "<file1> <file2>");
}

View file

@ -81,6 +81,7 @@ impl<'a> ArgMatcher<'a> {
}
pub fn inc_occurrence_of(&mut self, arg: &'a str) {
debugln!("fn=inc_occurrence_of;");
if let Some(a) = self.get_mut(arg) {
debugln!("+1 to {}'s occurrences", arg);
a.occurs += 1;
@ -90,6 +91,7 @@ impl<'a> ArgMatcher<'a> {
}
pub fn inc_occurrences_of(&mut self, args: &[&'a str]) {
debugln!("fn=inc_occurrences_of;");
for arg in args {
self.inc_occurrence_of(arg);
}
@ -107,16 +109,20 @@ impl<'a> ArgMatcher<'a> {
pub fn needs_more_vals<'b, A>(&self, o: &A) -> bool
where A: AnyArg<'a, 'b>
{
debugln!("fn=needs_more_vals;");
if let Some(ma) = self.get(o.name()) {
if let Some(num) = o.num_vals() {
debugln!("num_vals...{}", num);
return if o.is_set(ArgSettings::Multiple) {
((ma.vals.len() as u64) % num) != 0
} else {
num != (ma.vals.len() as u64)
};
} else if let Some(num) = o.max_vals() {
debugln!("max_vals...{}", num);
return !((ma.vals.len() as u64) > num);
} else if o.min_vals().is_some() {
debugln!("min_vals...true");
return true;
}
return o.is_set(ArgSettings::Multiple);

View file

@ -150,7 +150,7 @@ fn subcommands_and_args_of(p: &Parser) -> String {
// Then the positional args
for arg in p.positionals() {
debugln!("iter;arg={}", arg.name);
debugln!("iter;arg={}", arg.b.name);
let a = format!("\"{name}:{help}\" \\",
name = arg.b.name.to_ascii_uppercase(),
help = arg.b.help.unwrap_or(""));