Skip to content

Commit

Permalink
feat: allows specifying AllowLeadingHyphen style values, but only for…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
kbknapp committed Nov 20, 2016
1 parent cf9d6ce commit c0d70fe
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 109 deletions.
3 changes: 3 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
6 changes: 2 additions & 4 deletions src/app/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
109 changes: 28 additions & 81 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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`]
Expand Down Expand Up @@ -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<_> =
Expand All @@ -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) }
}
1 change: 1 addition & 0 deletions src/args/any_arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]>;
Expand Down
7 changes: 7 additions & 0 deletions src/args/arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 8 additions & 7 deletions src/args/arg_builder/flag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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[..]) }
Expand Down Expand Up @@ -98,31 +99,31 @@ 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");
}

#[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");
}

#[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");
}
Expand Down
23 changes: 12 additions & 11 deletions src/args/arg_builder/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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[..]) }
Expand Down Expand Up @@ -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>...");
}
Expand All @@ -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>");
}
Expand All @@ -166,27 +167,27 @@ 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>");
}

#[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>");
}

#[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>");
}
Expand Down
11 changes: 6 additions & 5 deletions src/args/arg_builder/positional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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[..]) }
Expand Down Expand Up @@ -134,15 +135,15 @@ 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>...");
}

#[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>");
}
Expand All @@ -153,19 +154,19 @@ 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>");
}

#[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>");
}
Expand Down
Loading

0 comments on commit c0d70fe

Please sign in to comment.