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/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("