diff --git a/src/lib.rs b/src/lib.rs index ba28fa2c..c3a9ef18 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,21 +127,21 @@ mod tests; /// A description of the options that a program can handle. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Options { - grps: Vec, +pub struct Options<'a> { + grps: Vec>, parsing_style: ParsingStyle, long_only: bool, } -impl Default for Options { +impl<'a> Default for Options<'a> { fn default() -> Self { Self::new() } } -impl Options { +impl<'a> Options<'a> { /// Create a blank set of options. - pub fn new() -> Options { + pub fn new() -> Self { Options { grps: Vec::new(), parsing_style: ParsingStyle::FloatingFrees, @@ -150,7 +150,7 @@ impl Options { } /// Set the parsing style. - pub fn parsing_style(&mut self, style: ParsingStyle) -> &mut Options { + pub fn parsing_style(&mut self, style: ParsingStyle) -> &mut Self { self.parsing_style = style; self } @@ -163,7 +163,7 @@ impl Options { /// of "-a=b"; in the ordinary mode this will parse a short option /// "-a" with argument "=b"; whereas in long-options-only mode the /// argument will be simply "b". - pub fn long_only(&mut self, long_only: bool) -> &mut Options { + pub fn long_only(&mut self, long_only: bool) -> &mut Self { self.long_only = long_only; self } @@ -171,19 +171,19 @@ impl Options { /// Create a generic option group, stating all parameters explicitly. pub fn opt( &mut self, - short_name: &str, - long_name: &str, - desc: &str, - hint: &str, + short_name: &'a str, + long_name: &'a str, + desc: &'a str, + hint: &'a str, hasarg: HasArg, occur: Occur, - ) -> &mut Options { + ) -> &mut Self { validate_names(short_name, long_name); self.grps.push(OptGroup { - short_name: short_name.to_string(), - long_name: long_name.to_string(), - hint: hint.to_string(), - desc: desc.to_string(), + short_name: short_name, + long_name: long_name, + hint: hint, + desc: desc, hasarg, occur, }); @@ -206,13 +206,13 @@ impl Options { /// let matches = opts.parse(&["-h"]).unwrap(); /// assert!(matches.opt_present("h")); /// ``` - pub fn optflag(&mut self, short_name: &str, long_name: &str, desc: &str) -> &mut Options { + pub fn optflag(&mut self, short_name: &'a str, long_name: &'a str, desc: &'a str) -> &mut Self { validate_names(short_name, long_name); self.grps.push(OptGroup { - short_name: short_name.to_string(), - long_name: long_name.to_string(), - hint: "".to_string(), - desc: desc.to_string(), + short_name: short_name, + long_name: long_name, + hint: "", + desc: desc, hasarg: No, occur: Optional, }); @@ -236,13 +236,18 @@ impl Options { /// let matches = opts.parse(&["-v", "--verbose"]).unwrap(); /// assert_eq!(2, matches.opt_count("v")); /// ``` - pub fn optflagmulti(&mut self, short_name: &str, long_name: &str, desc: &str) -> &mut Options { + pub fn optflagmulti( + &mut self, + short_name: &'a str, + long_name: &'a str, + desc: &'a str, + ) -> &mut Self { validate_names(short_name, long_name); self.grps.push(OptGroup { - short_name: short_name.to_string(), - long_name: long_name.to_string(), - hint: "".to_string(), - desc: desc.to_string(), + short_name: short_name, + long_name: long_name, + hint: "", + desc: desc, hasarg: No, occur: Multi, }); @@ -272,17 +277,17 @@ impl Options { /// ``` pub fn optflagopt( &mut self, - short_name: &str, - long_name: &str, - desc: &str, - hint: &str, - ) -> &mut Options { + short_name: &'a str, + long_name: &'a str, + desc: &'a str, + hint: &'a str, + ) -> &mut Self { validate_names(short_name, long_name); self.grps.push(OptGroup { - short_name: short_name.to_string(), - long_name: long_name.to_string(), - hint: hint.to_string(), - desc: desc.to_string(), + short_name: short_name, + long_name: long_name, + hint: hint, + desc: desc, hasarg: Maybe, occur: Optional, }); @@ -314,17 +319,17 @@ impl Options { /// ``` pub fn optmulti( &mut self, - short_name: &str, - long_name: &str, - desc: &str, - hint: &str, - ) -> &mut Options { + short_name: &'a str, + long_name: &'a str, + desc: &'a str, + hint: &'a str, + ) -> &mut Self { validate_names(short_name, long_name); self.grps.push(OptGroup { - short_name: short_name.to_string(), - long_name: long_name.to_string(), - hint: hint.to_string(), - desc: desc.to_string(), + short_name: short_name, + long_name: long_name, + hint: hint, + desc: desc, hasarg: Yes, occur: Multi, }); @@ -355,17 +360,17 @@ impl Options { /// ``` pub fn optopt( &mut self, - short_name: &str, - long_name: &str, - desc: &str, - hint: &str, - ) -> &mut Options { + short_name: &'a str, + long_name: &'a str, + desc: &'a str, + hint: &'a str, + ) -> &mut Self { validate_names(short_name, long_name); self.grps.push(OptGroup { - short_name: short_name.to_string(), - long_name: long_name.to_string(), - hint: hint.to_string(), - desc: desc.to_string(), + short_name: short_name, + long_name: long_name, + hint: hint, + desc: desc, hasarg: Yes, occur: Optional, }); @@ -398,17 +403,17 @@ impl Options { /// ``` pub fn reqopt( &mut self, - short_name: &str, - long_name: &str, - desc: &str, - hint: &str, - ) -> &mut Options { + short_name: &'a str, + long_name: &'a str, + desc: &'a str, + hint: &'a str, + ) -> &mut Self { validate_names(short_name, long_name); self.grps.push(OptGroup { - short_name: short_name.to_string(), - long_name: long_name.to_string(), - hint: hint.to_string(), - desc: desc.to_string(), + short_name: short_name, + long_name: long_name, + hint: hint, + desc: desc, hasarg: Yes, occur: Req, }); @@ -423,7 +428,7 @@ impl Options { /// /// Returns `Err(Fail)` on failure: use the `Debug` implementation of `Fail` /// to display information about it. - pub fn parse(&self, args: C) -> Result + pub fn parse(&self, args: C) -> Result<'a> where C::Item: AsRef, { @@ -529,9 +534,7 @@ impl Options { // FloatingFrees is in use. if let Some(i_arg) = i_arg.take() { vals[opt_id].push((arg_pos, Val(i_arg))); - } else if was_long - || args.peek().map_or(true, |n| is_arg(&n)) - { + } else if was_long || args.peek().map_or(true, |n| is_arg(&n)) { vals[opt_id].push((arg_pos, Given)); } else { vals[opt_id].push((arg_pos, Val(args.next().unwrap()))); @@ -565,7 +568,12 @@ impl Options { // in option does not exist in `free` and must be replaced with `None` args_end = args_end.filter(|pos| pos != &free.len()); - Ok(Matches { opts, vals, free, args_end }) + Ok(Matches { + opts, + vals, + free, + args_end, + }) } /// Derive a short one-line usage summary from a set of long options. @@ -603,7 +611,7 @@ impl Options { } /// Derive usage items from a set of options. - fn usage_items<'a>(&'a self) -> Box + 'a> { + fn usage_items<'b>(&'b self) -> Box + 'b> { let desc_sep = format!("\n{}", repeat(" ").take(24).collect::()); let any_short = self.grps.iter().any(|optref| !optref.short_name.is_empty()); @@ -709,10 +717,10 @@ pub enum ParsingStyle { /// Name of an option. Either a string or a single char. #[derive(Clone, Debug, PartialEq, Eq)] -enum Name { +enum Name<'a> { /// A string representing the long name of an option. /// For example: "help" - Long(String), + Long(&'a str), /// A char representing the short name of an option. /// For example: 'h' Short(char), @@ -742,29 +750,29 @@ pub enum Occur { /// A description of a possible option. #[derive(Clone, Debug, PartialEq, Eq)] -struct Opt { +struct Opt<'a> { /// Name of the option - name: Name, + name: Name<'a>, /// Whether it has an argument hasarg: HasArg, /// How often it can occur occur: Occur, /// Which options it aliases - aliases: Vec, + aliases: Vec>, } /// One group of options, e.g., both `-h` and `--help`, along with /// their shared description and properties. #[derive(Debug, Clone, PartialEq, Eq)] -struct OptGroup { +struct OptGroup<'a> { /// Short name of the option, e.g. `h` for a `-h` option - short_name: String, + short_name: &'a str, /// Long name of the option, e.g. `help` for a `--help` option - long_name: String, + long_name: &'a str, /// Hint for argument, e.g. `FILE` for a `-o FILE` option - hint: String, + hint: &'a str, /// Description for usage help text - desc: String, + desc: &'a str, /// Whether option has an argument hasarg: HasArg, /// How often it can occur @@ -781,9 +789,9 @@ enum Optval { /// The result of checking command line arguments. Contains a vector /// of matches and a vector of free strings. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Matches { +pub struct Matches<'a> { /// Options that matched - opts: Vec, + opts: Vec>, /// Values of the Options that matched and their positions vals: Vec>, @@ -814,14 +822,14 @@ pub enum Fail { impl Error for Fail {} /// The result of parsing a command line with a set of options. -pub type Result = result::Result; +pub type Result<'a> = result::Result, Fail>; -impl Name { - fn from_str(nm: &str) -> Name { +impl<'a> Name<'a> { + fn from_str(nm: &'a str) -> Name<'a> { if nm.len() == 1 { Short(nm.as_bytes()[0] as char) } else { - Long(nm.to_string()) + Long(nm) } } @@ -833,10 +841,10 @@ impl Name { } } -impl OptGroup { +impl<'a> OptGroup<'a> { /// Translate OptGroup into Opt. /// (Both short and long names correspond to different Opts). - fn long_to_short(&self) -> Opt { + fn long_to_short(&self) -> Opt<'a> { let OptGroup { short_name, long_name, @@ -875,7 +883,7 @@ impl OptGroup { } } -impl Matches { +impl<'a> Matches<'a> { fn opt_vals(&self, nm: &str) -> Vec<(usize, Optval)> { match find_opt(&self.opts, &Name::from_str(nm)) { Some(id) => self.vals[id].clone(), @@ -915,7 +923,10 @@ impl Matches { /// /// This function will panic if the option name is not defined. pub fn opt_positions(&self, name: &str) -> Vec { - self.opt_vals(name).into_iter().map(|(pos, _)| pos).collect() + self.opt_vals(name) + .into_iter() + .map(|(pos, _)| pos) + .collect() } /// Returns true if any of several options were matched. diff --git a/src/tests/mod.rs b/src/tests/mod.rs index f1eb9410..54405f42 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -248,7 +248,7 @@ fn test_free_trailing_only() { #[test] fn test_free_trailing_args() { - let args = vec!["pre".to_owned(), "--".to_owned(), "post".to_owned() ]; + let args = vec!["pre".to_owned(), "--".to_owned(), "post".to_owned()]; match Options::new().parse(&args) { Ok(ref m) => { assert_eq!(m.free_trailing_start(), Some(1)); @@ -741,10 +741,7 @@ fn test_multi() { ); assert_eq!(matches_both.opts_str_first(&["e"]).unwrap(), "foo"); - assert_eq!( - matches_both.opts_str_first(&["encrypt"]).unwrap(), - "bar" - ); + assert_eq!(matches_both.opts_str_first(&["encrypt"]).unwrap(), "bar"); assert_eq!( matches_both.opts_str_first(&["e", "encrypt"]).unwrap(), "foo" @@ -794,7 +791,7 @@ fn test_nospace_conflict() { #[test] fn test_long_to_short() { let mut short = Opt { - name: Name::Long("banana".to_string()), + name: Name::Long("banana"), hasarg: HasArg::Yes, occur: Occur::Req, aliases: Vec::new(), @@ -1319,3 +1316,18 @@ fn test_opt_strs_pos() { ] ); } + +#[test] +fn test_opt_names_and_desc_not_static() { + let short = "t".to_string(); + let long = "test".to_string(); + let desc = "test flag".to_string(); + let hint = "hint".to_string(); + + let args = vec!["--test".to_owned(), "value".to_owned()]; + let m = Options::new() + .optopt(&short, &long, &desc, &hint) + .parse(&args) + .unwrap(); + assert_eq!(m.opt_str("test").unwrap(), "value"); +}