diff --git a/CHANGELOG.md b/CHANGELOG.md index e30fcd1adf5..03a792a62c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ + +### v2.0.2 (2016-01-31) + + +#### Improvements + +* **arg_enum:** enum declared with arg_enum returns [&'static str; #] instead of Vec ([9c4b8a1a](https://github.com/kbknapp/clap-rs/commit/9c4b8a1a6b12949222f17d1074578ad7676b9c0d)) + +#### Bug Fixes + +* clap_app! should be gated by unstable, not nightly feature ([0c8b84af](https://github.com/kbknapp/clap-rs/commit/0c8b84af6161d5baf683688eafc00874846f83fa)) +* **SubCommands:** fixed where subcmds weren't recognized after mult args ([c19c17a8](https://github.com/kbknapp/clap-rs/commit/c19c17a8850602990e24347aeb4427cf43316223), closes [#405](https://github.com/kbknapp/clap-rs/issues/405)) +* **Usage Parser:** fixes a bug where literal single quotes weren't allowed in help strings ([0bcc7120](https://github.com/kbknapp/clap-rs/commit/0bcc71206478074769e311479b34a9f74fe80f5c), closes [#406](https://github.com/kbknapp/clap-rs/issues/406)) + + ### v2.0.1 (2016-01-30) diff --git a/Cargo.toml b/Cargo.toml index e977afbc94b..888c2ebcf3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clap" -version = "2.0.1" +version = "2.0.2" authors = ["Kevin K. "] exclude = ["examples/*", "clap-tests/*", "tests/*", "benches/*", "*.png", "clap-perf/*"] description = "A simple to use, efficient, and full featured Command Line Argument Parser" diff --git a/examples/13a_enum_values_automatic.rs b/examples/13a_enum_values_automatic.rs index 2b265bf97fe..13b8779ccf5 100644 --- a/examples/13a_enum_values_automatic.rs +++ b/examples/13a_enum_values_automatic.rs @@ -42,7 +42,7 @@ fn main() { let m = App::new("myapp") // Use a single positional argument that is required .arg(Arg::from_usage(" 'The Foo to use'") - .possible_values(&*Foo::variants())) + .possible_values(&Foo::variants())) .arg(Arg::from_usage(" 'The speed to use'") // You can define a list of possible values if you want the values to be // displayed in the help information. Whether you use possible_values() or diff --git a/src/app/parser.rs b/src/app/parser.rs index ecef0af76b9..b09e2d94f15 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -460,7 +460,8 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { // Has the user already passed '--'? if !pos_only { - if !starts_new_arg || self.is_set(AppSettings::AllowLeadingHyphen) { + let pos_sc = self.subcommands.iter().any(|s| &s.0.meta.name[..] == &*arg_os); + if (!starts_new_arg || self.is_set(AppSettings::AllowLeadingHyphen)) && !pos_sc { // Check to see if parsing a value from an option if let Some(nvo) = needs_val_of { // get the OptBuilder so we can check the settings @@ -488,13 +489,11 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { } } - // let arg_str = arg_os.to_str().expect(INVALID_UTF8); - if self.subcommands.iter().any(|s| &s.0.meta.name[..] == &*arg_os) { + if pos_sc { if &*arg_os == "help" && self.settings.is_set(AppSettings::NeedsSubcommandHelp) { return self._help(); } - // subcommands only support valid UTF-8 subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned()); break; } else if let Some(candidate) = suggestions::did_you_mean( @@ -618,7 +617,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { mid_string.push_str(" "); if let Some(ref mut sc) = self.subcommands .iter_mut() - .filter(|s| &s.0.meta.name[..] == &sc_name) + .filter(|s| &s.0.meta.name == &sc_name) .next() { let mut sc_matcher = ArgMatcher::new(); // bin_name should be parent's bin_name + [] + the sc's name separated by diff --git a/src/args/arg.rs b/src/args/arg.rs index d444fffc5f0..91648262a82 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -206,6 +206,9 @@ impl<'a, 'b> Arg<'a, 'b> { /// **NOTE**: Not all settings may be set using the usage string method. Some properties are /// only available via the builder pattern. /// + /// **NOTE**: Only ASCII values in `from_usage` strings are officially supported. Some UTF-8 + /// codepoints may work just fine, but this is not guaranteed. + /// /// # Syntax /// /// Usage strings typically following the form: diff --git a/src/lib.rs b/src/lib.rs index 1fc002ef78b..266eeb6d7b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,8 @@ +// Copyright ⓒ 2015-2016 Kevin B. Knapp and clap-rs contributors. +// Licensed under the MIT license +// (see LICENSE or ) All files in the project carrying such +// notice may not be copied, modified, or distributed except according to those terms. + //! A simple to use, efficient, and full featured library for parsing command line arguments and subcommands when writing console, or terminal applications. //! //! ## About diff --git a/src/macros.rs b/src/macros.rs index c26bbba3dbe..1c12f18b14f 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -212,6 +212,35 @@ macro_rules! values_t_or_exit { }; } +// _clap_count_exprs! is derived from https://github.com/DanielKeep/rust-grabbag +// commit: 82a35ca5d9a04c3b920622d542104e3310ee5b07 +// License: MIT +// Copyright ⓒ 2015 grabbag contributors. +// Licensed under the MIT license (see LICENSE or ) or the Apache License, Version 2.0 (see LICENSE of +// ), at your option. All +// files in the project carrying such notice may not be copied, modified, +// or distributed except according to those terms. +// +/// Counts the number of comma-delimited expressions passed to it. The result is a compile-time +/// evaluable expression, suitable for use as a static array size, or the value of a `const`. +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] extern crate clap; +/// # fn main() { +/// const COUNT: usize = _clap_count_exprs!(a, 5+1, "hi there!".into_string()); +/// assert_eq!(COUNT, 3); +/// # } +/// ``` +#[macro_export] +macro_rules! _clap_count_exprs { + () => { 0 }; + ($e:expr) => { 1 }; + ($e:expr, $($es:expr),+) => { 1 + _clap_count_exprs!($($es),*) }; +} + /// Convenience macro to generate more complete enums with variants to be used as a type when /// parsing arguments. This enum also provides a `variants()` function which can be used to /// retrieve a `Vec<&'static str>` of the variant names, as well as implementing `FromStr` and @@ -284,8 +313,8 @@ macro_rules! arg_enum { } impl $e { #[allow(dead_code)] - pub fn variants() -> Vec<&'static str> { - vec![ + pub fn variants() -> [&'static str; _clap_count_exprs!($(stringify!($v)),+)] { + [ $(stringify!($v),)+ ] } diff --git a/src/usage_parser.rs b/src/usage_parser.rs index 3515f9fc3ca..731255d4f27 100644 --- a/src/usage_parser.rs +++ b/src/usage_parser.rs @@ -168,8 +168,9 @@ impl<'a> UsageParser<'a> { fn help(&mut self, arg: &mut Arg<'a, 'a>) { debugln!("fn=help;"); - self.pos += 1; - self.stop_at(help_end); + self.stop_at(help_start); + self.start = self.pos+1; + self.pos = self.usage.len()-1; debugln!("setting help: {}", &self.usage[self.start..self.pos]); arg.help = Some(&self.usage[self.start..self.pos]); self.pos += 1; // Move to next byte to keep from thinking ending ' is a start @@ -177,26 +178,970 @@ impl<'a> UsageParser<'a> { } } - #[inline] - fn name_end(b: u32) -> bool { - // 93(]), 62(>) - b > b']' as u32 || b < b'>' as u32 || (b > b'>' as u32 && b < b']' as u32) +#[inline] +fn name_end(b: u32) -> bool { + // 93(]), 62(>) + b > b']' as u32 || b < b'>' as u32 || (b > b'>' as u32 && b < b']' as u32) +} + +#[inline] +fn token(b: u32) -> bool { + // 39('), 45(-), 46(.), 60(<), 91([) + b < 39 || b > 91 || (b > 46 && b < 91 && b != b'<' as u32) || (b > 39 && b < 45) +} + +#[inline] +fn long_end(b: u32) -> bool { + // 39('), 46(.), 60(<), 61(=), 91([) + (b < 39 && (b > 13 && b != b' ' as u32)) || b > 91 || (b > 61 && b < 91) || (b > 39 && b < 60 && b != 46) +} + +#[inline] +fn help_start(b: u32) -> bool { + // 39(') + b < 39 || b > 39 +} + +#[cfg(test)] +mod test { + use args::Arg; + use args::ArgSettings; + + #[test] + fn create_flag_usage() { + let a = Arg::from_usage("[flag] -f 'some help info'"); + assert_eq!(a.name, "flag"); + assert_eq!(a.short.unwrap(), 'f'); + assert!(a.long.is_none()); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(!a.is_set(ArgSettings::Multiple)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let b = Arg::from_usage("[flag] --flag 'some help info'"); + assert_eq!(b.name, "flag"); + assert_eq!(b.long.unwrap(), "flag"); + assert!(b.short.is_none()); + assert_eq!(b.help.unwrap(), "some help info"); + assert!(!b.is_set(ArgSettings::Multiple)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let b = Arg::from_usage("--flag 'some help info'"); + assert_eq!(b.name, "flag"); + assert_eq!(b.long.unwrap(), "flag"); + assert!(b.short.is_none()); + assert_eq!(b.help.unwrap(), "some help info"); + assert!(!b.is_set(ArgSettings::Multiple)); + assert!(b.val_names.is_none()); + assert!(b.num_vals.is_none()); + + let c = Arg::from_usage("[flag] -f --flag 'some help info'"); + assert_eq!(c.name, "flag"); + assert_eq!(c.short.unwrap(), 'f'); + assert_eq!(c.long.unwrap(), "flag"); + assert_eq!(c.help.unwrap(), "some help info"); + assert!(!c.is_set(ArgSettings::Multiple)); + assert!(c.val_names.is_none()); + assert!(c.num_vals.is_none()); + + let d = Arg::from_usage("[flag] -f... 'some help info'"); + assert_eq!(d.name, "flag"); + assert_eq!(d.short.unwrap(), 'f'); + assert!(d.long.is_none()); + assert_eq!(d.help.unwrap(), "some help info"); + assert!(d.is_set(ArgSettings::Multiple)); + assert!(d.val_names.is_none()); + assert!(d.num_vals.is_none()); + + let e = Arg::from_usage("[flag] -f --flag... 'some help info'"); + assert_eq!(e.name, "flag"); + assert_eq!(e.long.unwrap(), "flag"); + assert_eq!(e.short.unwrap(), 'f'); + assert_eq!(e.help.unwrap(), "some help info"); + assert!(e.is_set(ArgSettings::Multiple)); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); + + let e = Arg::from_usage("-f --flag... 'some help info'"); + assert_eq!(e.name, "flag"); + assert_eq!(e.long.unwrap(), "flag"); + assert_eq!(e.short.unwrap(), 'f'); + assert_eq!(e.help.unwrap(), "some help info"); + assert!(e.is_set(ArgSettings::Multiple)); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); + + let e = Arg::from_usage("--flags"); + assert_eq!(e.name, "flags"); + assert_eq!(e.long.unwrap(), "flags"); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); + + let e = Arg::from_usage("--flags..."); + assert_eq!(e.name, "flags"); + assert_eq!(e.long.unwrap(), "flags"); + assert!(e.is_set(ArgSettings::Multiple)); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); + + let e = Arg::from_usage("[flags] -f"); + assert_eq!(e.name, "flags"); + assert_eq!(e.short.unwrap(), 'f'); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); + + let e = Arg::from_usage("[flags] -f..."); + assert_eq!(e.name, "flags"); + assert_eq!(e.short.unwrap(), 'f'); + assert!(e.is_set(ArgSettings::Multiple)); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); + + let a = Arg::from_usage("-f 'some help info'"); + assert_eq!(a.name, "f"); + assert_eq!(a.short.unwrap(), 'f'); + assert!(a.long.is_none()); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(!a.is_set(ArgSettings::Multiple)); + assert!(a.val_names.is_none()); + assert!(a.num_vals.is_none()); + + let e = Arg::from_usage("-f"); + assert_eq!(e.name, "f"); + assert_eq!(e.short.unwrap(), 'f'); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); + + let e = Arg::from_usage("-f..."); + assert_eq!(e.name, "f"); + assert_eq!(e.short.unwrap(), 'f'); + assert!(e.is_set(ArgSettings::Multiple)); + assert!(e.val_names.is_none()); + assert!(e.num_vals.is_none()); + } + + #[test] + fn create_option_usage0() { + // Short only + let a = Arg::from_usage("[option] -o [opt] 'some help info'"); + assert_eq!(a.name, "option"); + assert_eq!(a.short.unwrap(), 'o'); + assert!(a.long.is_none()); + assert_eq!(a.help.unwrap(), "some help info"); + assert!(!a.is_set(ArgSettings::Multiple)); + assert!(a.is_set(ArgSettings::TakesValue)); + assert!(!a.is_set(ArgSettings::Required)); + assert_eq!(a.val_names.unwrap().iter().map(|(_, &v)| v).collect::>(), ["opt"]); + assert!(a.num_vals.is_none()); + } + + #[test] + fn create_option_usage1() { + let b = Arg::from_usage("-o [opt] 'some help info'"); + assert_eq!(b.name, "o"); + assert_eq!(b.short.unwrap(), 'o'); + assert!(b.long.is_none()); + assert_eq!(b.help.unwrap(), "some help info"); + assert!(!b.is_set(ArgSettings::Multiple)); + assert!(b.is_set(ArgSettings::TakesValue)); + assert!(!b.is_set(ArgSettings::Required)); + assert_eq!(b.val_names.unwrap().iter().map(|(_, &v)| v).collect::>(), ["opt"]); + assert!(b.num_vals.is_none()); + } + + #[test] + fn create_option_usage2() { + let c = Arg::from_usage("