From 69b14e67ff837544dbec36d2f4e82ff22653cad8 Mon Sep 17 00:00:00 2001 From: acdenisSK Date: Sat, 2 Jun 2018 13:58:57 +0200 Subject: [PATCH 01/10] Move to a lexer-approach --- benches/bench_args.rs | 50 --- src/framework/standard/args.rs | 545 +++++++++++++++++++-------------- 2 files changed, 323 insertions(+), 272 deletions(-) diff --git a/benches/bench_args.rs b/benches/bench_args.rs index c870294d19b..da05c4eab07 100644 --- a/benches/bench_args.rs +++ b/benches/bench_args.rs @@ -48,56 +48,6 @@ mod benches { }) } - #[bench] - fn len_with_one_delimiter(b: &mut Bencher) { - b.iter(|| { - let mut args = Args::new("1,2,3,4,5,6,7,8,9,10,11,12,13,14", &[",".to_string()]); - args.len(); - }) - } - - #[bench] - fn double_len_with_one_delimiter(b: &mut Bencher) { - b.iter(|| { - let mut args = Args::new("1,2,3,4,5,6,7,8,9,10,11,12,13,14", &[",".to_string()]); - args.len(); - args.len(); - }) - } - - #[bench] - fn double_len_quoted_with_one_delimiter(b: &mut Bencher) { - b.iter(|| { - let mut args = Args::new(r#""a" "a" "a" "a" "a" "a" "a" "a" "a" "a""#, &[" ".to_string()]); - args.len_quoted(); - args.len_quoted(); - }) - } - - #[bench] - fn len_with_three_delimiter(b: &mut Bencher) { - b.iter(|| { - let mut args = Args::new("1,2,3,4@5,6,7@8,9,10@11,12#13,14", &[",".to_string(), "@".to_string(), "#".to_string()]); - args.len(); - }) - } - - #[bench] - fn len_quoted_with_one_delimiter(b: &mut Bencher) { - b.iter(|| { - let mut args = Args::new(r#""1","2","3","4","5","6","7","8","9","10""#, &[",".to_string()]); - args.len(); - }) - } - - #[bench] - fn len_quoted_with_three_delimiter(b: &mut Bencher) { - b.iter(|| { - let mut args = Args::new(r#""1"-"2"<"3","4","5","6","7"<"8","9"<"10""#, &[",".to_string(), "-".to_string(), "<".to_string()]); - args.len(); - }) - } - #[bench] fn multiple_with_one_delimiter(b: &mut Bencher) { b.iter(|| { diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index 701b7f2822e..18940628ab1 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -53,103 +53,192 @@ impl fmt::Display for Error { type Result = ::std::result::Result>; -fn second_quote_occurence(s: &str) -> Option { - s.match_indices('"').nth(1).map(|(pos, _)| pos) +fn find_end(s: &str, i: usize) -> Option { + if i > s.len() { + return None; + } + + let mut end = i + 1; + while !s.is_char_boundary(end) { + end += 1; + } + + Some(end) } -fn parse_quotes(s: &mut String, delimiters: &[String]) -> Result - where T::Err: StdError { - - if s.is_empty() { - return Err(Error::Eos); - } - - // Fall back to `parse` if there're no quotes at the start - // or if there is no closing one as well. - if let Some(mut pos) = second_quote_occurence(s) { - if s.starts_with('"') { - let res = (&s[1..pos]).parse::().map_err(Error::Parse)?; - pos += '"'.len_utf8(); - - for delimiter in delimiters { - if s[pos..].starts_with(delimiter) { - pos += delimiter.len(); - break; - } - } +#[derive(Debug, Clone, Copy, PartialEq)] +enum TokenKind { + Delimiter, + Argument, + QuotedArgument, + Eof, +} + +#[derive(Debug)] +struct Token<'a> { + lit: &'a str, + kind: TokenKind, +} - s.drain(..pos); +impl<'a> Token<'a> { + fn new(kind: TokenKind, lit: &'a str) -> Self { + Token { kind, lit } + } + + fn empty() -> Self { + Token { + kind: TokenKind::Eof, + lit: "", + } + } +} - return Ok(res); +#[derive(Debug, Clone)] +struct TokenOwned { + kind: TokenKind, + lit: String, +} + +impl<'a> Token<'a> { + fn to_owned(&self) -> TokenOwned { + TokenOwned { + kind: self.kind, + lit: self.lit.to_string(), } } +} - parse::(s, delimiters) +impl PartialEq for TokenOwned { + fn eq(&self, other: &TokenKind) -> bool { + self.kind == *other + } } +#[derive(Debug)] +struct Lexer<'a> { + msg: &'a str, + delims: &'a [char], + offset: usize, +} -fn parse(s: &mut String, delimiters: &[String]) -> Result - where T::Err: StdError { - if s.is_empty() { - return Err(Error::Eos); +impl<'a> Lexer<'a> { + fn new(msg: &'a str, delims: &'a [char]) -> Self { + Lexer { + msg, + delims, + offset: 0, + } } - let (mut smallest_pos, delimiter_len) = delimiters.iter().fold((s.len(), 0usize), |mut acc, delim| { - let other_pos = s.find(delim).unwrap_or_else(|| s.len()); + fn at_end(&self) -> bool { + self.offset >= self.msg.len() + } - if acc.0 > other_pos { - acc.0 = other_pos; - acc.1 = delim.len(); + fn current(&self) -> Option<&str> { + if self.at_end() { + return None; } - acc - }); + let start = self.offset; + + let end = find_end(&self.msg, self.offset)?; - let res = (&s[..smallest_pos]).parse::().map_err(Error::Parse)?; + Some(&self.msg[start..end]) + } + + fn next(&mut self) -> Option<()> { + self.offset += self.current()?.len(); - if smallest_pos < s.len() { - smallest_pos += delimiter_len; + Some(()) } - s.drain(..smallest_pos); + fn commit(&mut self) -> Token<'a> { + if self.at_end() { + return Token::empty(); + } + + if self.current().unwrap().contains(self.delims) { + let start = self.offset; + self.next(); + return Token::new(TokenKind::Delimiter, &self.msg[start..self.offset]); + } + + if self.current().unwrap() == "\"" { + let start = self.offset; + self.next(); + + while !self.at_end() && self.current().unwrap() != "\"" { + self.next(); + } + + self.next(); + let end = self.offset; + + return if self.at_end() && &self.msg[end-1..end] != "\"" { + // invalid, missing an end quote; view it as a normal argument instead. + Token::new(TokenKind::Argument, &self.msg[start..]) + } else { + Token::new(TokenKind::QuotedArgument, &self.msg[start..end]) + }; + } + + let start = self.offset; + + while !self.at_end() && !self.current().unwrap().contains(self.delims) { + self.next(); + } - Ok(res) + Token::new(TokenKind::Argument, &self.msg[start..self.offset]) + } } /// A utility struct for handling arguments of a command. /// /// An "argument" is a part of the message up until the end of the message or at one of the specified delimiters. -/// For instance, with a space delimiter (" ") in a message like "ab cd", we would get the argument "ab", and then "cd". +/// For instance, in a message like "ab cd" with given a space delimiter (" "), we'd get the arguments "ab" then "cd". /// -/// For the most part, almost all methods provided by this struct not only make arguments convenient to handle, -/// they'll also parse your argument to a specific type if you need to work with the type itself and not some shady string. -/// -/// And for another part, in case you need multiple things, whether delimited or not, gobled in one argument, -/// you can utilize the `*_quoted` methods that will extract anything inside quotes for you. -/// Though they'll fall back to the original behaviour of, for example, `single`, -/// on the occasion that the quotes are malformed (missing a starting or ending quote). +/// In addition, the methods parse your argument to a certain type you gave to improve ergonomics. +/// To further add, for cases where you stumble upon the need for quotes, consider using the `*_quoted` methods. /// -/// # Catch regarding how `Args` functions /// -/// Majority of the methods here internally chop of the argument (i.e you won't encounter it anymore), to advance to further arguments. -/// If you do not desire for this behaviour, consider using the suffixed `*_n` methods instead. +/// # A catch about how `Args` functions +/// Most of the methods advance to the next argument once the job on the current one is done. +/// If this is not something you desire, you have 2 options in your arsenal: +/// 1. To not advance at all, you can use the `*_n` methods, or; +/// 2. you can go back one step with the `rewind` method, or completely (to the start) with the `restore` method. #[derive(Clone, Debug)] pub struct Args { - delimiters: Vec, message: String, - len: Option, - len_quoted: Option, + args: Vec, + offset: usize, } impl Args { pub fn new(message: &str, possible_delimiters: &[String]) -> Self { + let delims = possible_delimiters + .iter() + .filter(|d| message.contains(d.as_str())) + .flat_map(|s| s.chars()) + .collect::>(); + + let mut lex = Lexer::new(message, &delims); + + let mut args = Vec::new(); + + while !lex.at_end() { + let token = lex.commit(); + + if token.kind == TokenKind::Delimiter { + continue; + } + + args.push(token.to_owned()); + } + Args { - delimiters: possible_delimiters - .iter() - .filter(|&d| message.contains(d)).cloned().collect(), + args, message: message.to_string(), - len: None, - len_quoted: None, + offset: 0, } } @@ -162,8 +251,10 @@ impl Args { /// /// let mut args = Args::new("42 69", &[" ".to_string()]); /// - /// assert_eq!(args.single::().unwrap(), 42); - /// assert_eq!(args.full(), "69"); + /// assert_eq!(args.single::().unwrap(), 42); + /// + /// // `42` is now out of the way, next we have `69` + /// assert_eq!(args.single::().unwrap(), 69); /// ``` pub fn single(&mut self) -> Result where T::Err: StdError { @@ -171,11 +262,11 @@ impl Args { return Err(Error::Eos); } - if let Some(ref mut val) = self.len { - *val -= 1 - } + let cur = &self.args[self.offset]; - parse::(&mut self.message, &self.delimiters) + let parsed = T::from_str(&cur.lit)?; + self.offset += 1; + Ok(parsed) } /// Like [`single`], but doesn't advance. @@ -187,17 +278,23 @@ impl Args { /// /// let args = Args::new("42 69", &[" ".to_string()]); /// - /// assert_eq!(args.single_n::().unwrap(), 42); + /// assert_eq!(args.single_n::().unwrap(), 42); /// assert_eq!(args, "42 69"); /// ``` /// /// [`single`]: #method.single pub fn single_n(&self) -> Result where T::Err: StdError { - parse::(&mut self.message.clone(), &self.delimiters) + if self.is_empty() { + return Err(Error::Eos); + } + + let cur = &self.args[self.offset]; + + Ok(T::from_str(&cur.lit)?) } - /// Accesses the current state of the internally-stored message. + /// Gets original message passed to the command. /// /// # Examples /// @@ -208,11 +305,12 @@ impl Args { /// /// assert_eq!(args.full(), "42 69"); /// ``` - pub fn full(&self) -> &str { &self.message } + pub fn full(&self) -> &str { + &self.message + } - /// Accesses the current state of the internally-stored message, - /// removing quotes if it contains the opening and closing ones, - /// but otherwise returns the string as is. + /// Gets the original message passed to the command, + /// but without quotes (if both starting and ending quotes are present, otherwise returns as is). /// /// # Examples /// @@ -261,7 +359,7 @@ impl Args { &s[1..end] } - /// The amount of args. + /// The amount of arguments. /// /// # Examples /// @@ -272,25 +370,34 @@ impl Args { /// /// assert_eq!(args.len(), 2); // `2` because `["42", "69"]` /// ``` - pub fn len(&mut self) -> usize { - if let Some(len) = self.len { - len - } else if self.is_empty() { - 0 - } else { - let mut words: Box> = Box::new(Some(&self.message[..]).into_iter()); - - for delimiter in &self.delimiters { - words = Box::new(words.flat_map(move |x| x.split(delimiter))); - } + pub fn len(&self) -> usize { + self.args.len() + } - let len = words.count(); - self.len = Some(len); - len + /// Amount of arguments still available. + /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let mut args = Args::new("42 69", &[" ".to_string()]); + /// + /// assert_eq!(args.remaining(), 2); + /// + /// args.skip(); + /// + /// assert_eq!(args.remaining(), 1); + /// ``` + pub fn remaining(&self) -> usize { + if self.is_empty() { + return 0; } + + self.len() - self.offset } - /// Returns true if the string is empty or else false. + /// Returns true if there are no arguments left. /// /// # Examples /// @@ -299,10 +406,65 @@ impl Args { /// /// let mut args = Args::new("", &[" ".to_string()]); /// - /// assert!(args.is_empty()); // `true` because passed message is empty. + /// assert!(args.is_empty()); // `true` because passed message is empty thus no arguments. /// ``` pub fn is_empty(&self) -> bool { - self.message.is_empty() + self.offset >= self.args.len() + } + + /// Go one step behind + /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let mut args = Args::new("42 69", &[" ".to_string()]); + /// + /// assert_eq!(args.single::().unwrap(), 42); + /// + /// // By this point, we can only parse 69 now. + /// // However, with the help of `rewind`, we can mess with 42 again. + /// args.rewind(); + /// + /// assert_eq!(args.single::().unwrap() * 2, 84); + /// ``` + #[inline] + pub fn rewind(&mut self) { + if self.offset == 0 { + return; + } + + self.offset -= 1; + } + + /// Go back to the starting point. + /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let mut args = Args::new("42 69 95", &[" ".to_string()]); + /// + /// // Lets parse 'em numbers! + /// assert_eq!(args.single::().unwrap(), 42); + /// assert_eq!(args.single::().unwrap(), 69); + /// assert_eq!(args.single::().unwrap(), 95); + /// + /// // Oh, no! I actually wanted to multiply all of them by 2! + /// // I don't want to call `rewind` 3 times manually.... + /// // Wait, i could just go entirely back! + /// args.restore(); + /// + /// assert_eq!(args.single::().unwrap() * 2, 84); + /// assert_eq!(args.single::().unwrap() * 2, 138); + /// assert_eq!(args.single::().unwrap() * 2, 190); + /// ``` + /// + #[inline] + pub fn restore(&mut self) { + self.offset = 0; } /// Like [`len`], but accounts quotes. @@ -316,25 +478,12 @@ impl Args { /// /// assert_eq!(args.len_quoted(), 2); // `2` because `["42", "69"]` /// ``` + #[deprecated(since = "0.5.3", note = "its task was merged with `len`, please use it instead.")] pub fn len_quoted(&mut self) -> usize { - if self.is_empty() { - 0 - } else if let Some(len_quoted) = self.len_quoted { - len_quoted - } else { - let countable_self = self.clone(); - - if let Ok(ref vec) = countable_self.multiple_quoted::() { - vec.iter().count() - } else { - 0 - } - } + self.len() } - /// Returns the argument as a string (thus sort-of skipping it). - /// - /// *This is sugar for `args.single::().ok()`* + /// "Skip" the argument (Sugar for `args.single::().ok()`) /// /// # Examples /// @@ -343,24 +492,18 @@ impl Args { /// /// let mut args = Args::new("42 69", &[" ".to_string()]); /// - /// assert_eq!(args.skip().unwrap(), "42"); - /// assert_eq!(args.full(), "69"); + /// args.skip(); + /// assert_eq!(args.single::().unwrap(), 69); /// ``` pub fn skip(&mut self) -> Option { if self.is_empty() { return None; } - if let Some(ref mut val) = self.len { - if 1 <= *val { - *val -= 1 - } - }; - - parse::(&mut self.message, &self.delimiters).ok() + self.single::().ok() } - /// Like [`skip`], but allows for multiple at once. + /// Like [`skip`], but do it multiple times. /// /// # Examples /// @@ -369,8 +512,9 @@ impl Args { /// /// let mut args = Args::new("42 69 88 99", &[" ".to_string()]); /// - /// assert_eq!(*args.skip_for(3).unwrap(), ["42".to_string(), "69".to_string(), "88".to_string()]); - /// assert_eq!(args, "99"); + /// args.skip_for(3); + /// assert_eq!(args.remaining(), 1); + /// assert_eq!(args.single::().unwrap(), 99); /// ``` /// /// [`skip`]: #method.skip @@ -385,15 +529,6 @@ impl Args { vec.push(self.skip()?); } - if let Some(ref mut val) = self.len { - - if i as usize <= *val { - *val -= i as usize - } else { - *val = 0 - } - } - Some(vec) } @@ -417,11 +552,17 @@ impl Args { return Err(Error::Eos); } - if let Some(ref mut val) = self.len_quoted { - *val -= 1 - } + let cur = &self.args[self.offset]; - parse_quotes::(&mut self.message, &self.delimiters) + let lit = if cur.kind == TokenKind::QuotedArgument { + &cur.lit[1..cur.lit.len() - 1] + } else { + &cur.lit + }; + + let parsed = T::from_str(&lit)?; + self.offset += 1; + Ok(parsed) } /// Like [`single_quoted`], but doesn't advance. @@ -440,7 +581,19 @@ impl Args { /// [`single_quoted`]: #method.single_quoted pub fn single_quoted_n(&self) -> Result where T::Err: StdError { - parse_quotes::(&mut self.message.clone(), &self.delimiters) + if self.is_empty() { + return Err(Error::Eos); + } + + let cur = &self.args[self.offset]; + + let lit = if cur.kind == TokenKind::QuotedArgument { + &cur.lit[1..cur.lit.len() - 1] + } else { + &cur.lit + }; + + Ok(T::from_str(&lit)?) } /// Like [`multiple`], but accounts quotes. @@ -452,7 +605,7 @@ impl Args { /// /// let mut args = Args::new(r#""42" "69""#, &[" ".to_string()]); /// - /// assert_eq!(*args.multiple_quoted::().unwrap(), [42, 69]); + /// assert_eq!(*args.multiple_quoted::().unwrap(), [42, 69]); /// ``` /// /// [`multiple`]: #method.multiple @@ -474,7 +627,7 @@ impl Args { /// /// let mut args = Args::new(r#""2" "5""#, &[" ".to_string()]); /// - /// assert_eq!(*args.iter_quoted::().map(|n| n.unwrap().pow(2)).collect::>(), [4, 25]); + /// assert_eq!(*args.iter_quoted::().map(|n| n.unwrap().pow(2)).collect::>(), [4, 25]); /// assert!(args.is_empty()); /// ``` /// @@ -484,9 +637,7 @@ impl Args { IterQuoted::new(self) } - /// This is a convenience function for parsing until the end of the message and returning the parsed results in a `Vec`. - /// - /// *This is sugar for `args.iter().collect::>()`* + /// Parses all of the remaining arguments and returns them in a `Vec` (Sugar for `args.iter().collect::>()`). /// /// # Examples /// @@ -495,7 +646,7 @@ impl Args { /// /// let args = Args::new("42 69", &[" ".to_string()]); /// - /// assert_eq!(*args.multiple::().unwrap(), [42, 69]); + /// assert_eq!(*args.multiple::().unwrap(), [42, 69]); /// ``` pub fn multiple(mut self) -> Result, T::Err> where T::Err: StdError { @@ -506,7 +657,7 @@ impl Args { self.iter::().collect() } - /// Provides an arguments iterator up until the end of the message. + /// Provides an iterator that will spew arguments until the end of the message. /// /// # Examples /// @@ -515,7 +666,7 @@ impl Args { /// /// let mut args = Args::new("3 4", &[" ".to_string()]); /// - /// assert_eq!(*args.iter::().map(|num| num.unwrap().pow(2)).collect::>(), [9, 16]); + /// assert_eq!(*args.iter::().map(|num| num.unwrap().pow(2)).collect::>(), [9, 16]); /// assert!(args.is_empty()); /// ``` pub fn iter(&mut self) -> Iter @@ -534,61 +685,28 @@ impl Args { /// ```rust /// use serenity::framework::standard::Args; /// - /// let mut args = Args::new("c47 69", &[" ".to_string()]); + /// let mut args = Args::new("c42 69", &[" ".to_string()]); /// - /// assert_eq!(args.find::().unwrap(), 69); - /// assert_eq!(args.full(), "c47"); + /// assert_eq!(args.find::().unwrap(), 69); + /// assert_eq!(args.single::().unwrap(), "c42"); + /// assert!(args.is_empty()); /// ``` pub fn find(&mut self) -> Result where T::Err: StdError { - // TODO: Make this efficient - - if self.delimiters.len() == 1 { - match self.message.split(&self.delimiters[0]).position(|e| e.parse::().is_ok()) { - Some(index) => { - fn do_stuff(msg: &str, delim: &str, index: usize) -> (String, String) { - let mut vec = msg.split(delim).collect::>(); - - let found = vec.remove(index); - let new_state = vec.join(delim); - - (found.to_string(), new_state) - } - - let (mut s, msg) = do_stuff(&self.message, &self.delimiters[0], index); - let res = parse::(&mut s, &self.delimiters); - self.message = msg; - - if let Some(ref mut val) = self.len { if 1 <= *val { *val -= 1 } }; - - res - }, - None => Err(Error::Eos), - } - } else { - let msg = self.message.clone(); - let mut words: Box> = Box::new(Some(&msg[..]).into_iter()); - - for delimiter in &self.delimiters { - words = Box::new(words.flat_map(move |x| x.split(delimiter))); - } + if self.is_empty() { + return Err(Error::Eos); + } - let mut words: Vec<&str> = words.collect(); - let pos = words.iter().position(|e| e.parse::().is_ok()); - if let Some(ref mut val) = self.len { if 1 <= *val { *val -= 1 } }; + let pos = match self.args.iter().position(|s| s.lit.parse::().is_ok()) { + Some(p) => p, + None => return Err(Error::Eos), + }; - match pos { - Some(index) => { - let ss = words.remove(index); + let parsed = T::from_str(&self.args[pos].lit)?; + self.args.remove(pos); + self.rewind(); - let res = parse::(&mut ss.to_string(), &self.delimiters); - self.len = Some(words.len()); - self.message = words.join(&self.delimiters[0]); - res - }, - None => Err(Error::Eos), - } - } + Ok(parsed) } /// Like [`find`], but does not remove it. @@ -598,46 +716,29 @@ impl Args { /// ```rust /// use serenity::framework::standard::Args; /// - /// let mut args = Args::new("c47 69", &[" ".to_string()]); + /// let mut args = Args::new("c42 69", &[" ".to_string()]); /// - /// assert_eq!(args.find_n::().unwrap(), 69); - /// assert_eq!(args.full(), "c47 69"); + /// assert_eq!(args.find_n::().unwrap(), 69); + /// + /// // The `69` is still here, so let's parse it again. + /// assert_eq!(args.single::().unwrap(), "c42"); + /// assert_eq!(args.single::().unwrap(), 69); + /// assert!(args.is_empty()); /// ``` /// /// [`find`]: #method.find pub fn find_n(&mut self) -> Result where T::Err: StdError { - // Same here. - if self.delimiters.len() == 1 { - let pos = self.message - .split(&self.delimiters[0]) - .position(|e| e.parse::().is_ok()); - - match pos { - Some(index) => { - let ss = self.message.split(&self.delimiters[0]).nth(index).unwrap(); - parse::(&mut ss.to_string(), &self.delimiters) - }, - None => Err(Error::Eos), - } - } else { - let mut words: Box> = Box::new(Some(&self.message[..]).into_iter()); - for delimiter in &self.delimiters { - words = Box::new(words.flat_map(move |x| x.split(delimiter))); - } + if self.is_empty() { + return Err(Error::Eos); + } - let pos = words.position(|e| e.parse::().is_ok()); - let mut words: Vec<&str> = words.collect(); + let pos = match self.args.iter().position(|s| s.lit.parse::().is_ok()) { + Some(p) => p, + None => return Err(Error::Eos), + }; - match pos { - Some(index) => { - let ss = words.remove(index); - self.len = Some(words.len()); - parse::(&mut ss.to_string(), &self.delimiters) - }, - None => Err(Error::Eos), - } - } + Ok(T::from_str(&self.args[pos].lit)?) } } @@ -669,7 +770,7 @@ impl Eq for Args {} use std::marker::PhantomData; -/// Provides `list`'s functionality, but as an iterator. +/// Parse each argument individually, as an iterator. pub struct Iter<'a, T: FromStr> where T::Err: StdError { args: &'a mut Args, _marker: PhantomData, From 923759383a53ac80be8751b5c45da9d0752c383a Mon Sep 17 00:00:00 2001 From: acdenisSK Date: Sat, 2 Jun 2018 19:47:23 +0200 Subject: [PATCH 02/10] ORDER --- src/framework/standard/args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index 18940628ab1..55243ad42d6 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -195,7 +195,7 @@ impl<'a> Lexer<'a> { /// A utility struct for handling arguments of a command. /// /// An "argument" is a part of the message up until the end of the message or at one of the specified delimiters. -/// For instance, in a message like "ab cd" with given a space delimiter (" "), we'd get the arguments "ab" then "cd". +/// For instance, in a message like "ab cd" with a given space delimiter (" "), we'd get the arguments "ab" then "cd". /// /// In addition, the methods parse your argument to a certain type you gave to improve ergonomics. /// To further add, for cases where you stumble upon the need for quotes, consider using the `*_quoted` methods. From 5e1790694029e92b5c0d29354ec9c694fae3ab4c Mon Sep 17 00:00:00 2001 From: acdenisSK Date: Sat, 2 Jun 2018 20:37:08 +0200 Subject: [PATCH 03/10] Fix tests --- tests/test_args.rs | 87 ++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/tests/test_args.rs b/tests/test_args.rs index 6911dae1c61..9868b1b5026 100644 --- a/tests/test_args.rs +++ b/tests/test_args.rs @@ -179,14 +179,14 @@ fn multiple_quoted_i32() { fn multiple_quoted_quote_appears_without_delimiter_in_front() { let args = Args::new(r#"hello, my name is cake" 2"#, &[",".to_string(), " ".to_string()]); - assert_eq!(args.multiple_quoted::().unwrap(), ["hello", "", "my", "name", "is", "cake\"", "2"]); + assert_eq!(args.multiple_quoted::().unwrap(), ["hello", "my", "name", "is", "cake\"", "2"]); } #[test] fn multiple_quoted_single_quote() { let args = Args::new(r#"hello "2 b"#, &[",".to_string(), " ".to_string()]); - assert_eq!(args.multiple_quoted::().unwrap(), ["hello", "\"2", "b"]); + assert_eq!(args.multiple_quoted::().unwrap(), ["hello", "\"2 b"]); } #[test] @@ -201,7 +201,7 @@ fn multiple_quoted_one_quote_pair() { fn delimiter_before_multiple_quoted() { let args = Args::new(r#","hello, my name is cake" "2""#, &[",".to_string(), " ".to_string()]); - assert_eq!(args.multiple_quoted::().unwrap(), ["", "hello, my name is cake", "2"]); + assert_eq!(args.multiple_quoted::().unwrap(), ["hello, my name is cake", "2"]); } #[test] @@ -274,7 +274,8 @@ fn skip() { let mut args = Args::new("1 2", &[" ".to_string()]); assert_eq!(args.skip().unwrap(), "1"); - assert_eq!(args.full(), "2"); + assert_eq!(args.remaining(), 1); + assert_eq!(args.single::().unwrap(), "2"); } #[test] @@ -282,100 +283,102 @@ fn skip_for() { let mut args = Args::new("1 2 neko 100", &[" ".to_string()]); assert_eq!(args.skip_for(2).unwrap(), ["1", "2"]); - assert_eq!(args.full(), "neko 100"); + assert_eq!(args.remaining(), 2); + assert_eq!(args.single::().unwrap(), "neko"); + assert_eq!(args.single::().unwrap(), "100"); } #[test] fn len_with_one_delimiter() { - let mut args = Args::new("1 2 neko 100", &[" ".to_string()]); + let args = Args::new("1 2 neko 100", &[" ".to_string()]); assert_eq!(args.len(), 4); - assert_eq!(args.len(), 4); + assert_eq!(args.remaining(), 4); } #[test] fn len_multiple_quoted() { - let mut args = Args::new(r#""hello, my name is cake" "2""#, &[" ".to_string()]); + let args = Args::new(r#""hello, my name is cake" "2""#, &[" ".to_string()]); - assert_eq!(args.len_quoted(), 2); + assert_eq!(args.len(), 2); } #[test] -fn len_before_and_after_single() { +fn remaining_len_before_and_after_single() { let mut args = Args::new("1 2", &[" ".to_string()]); - assert_eq!(args.len(), 2); + assert_eq!(args.remaining(), 2); assert_eq!(args.single::().unwrap(), 1); - assert_eq!(args.len(), 1); + assert_eq!(args.remaining(), 1); assert_eq!(args.single::().unwrap(), 2); - assert_eq!(args.len(), 0); + assert_eq!(args.remaining(), 0); } #[test] -fn len_before_and_after_single_quoted() { +fn remaining_len_before_and_after_single_quoted() { let mut args = Args::new(r#""1" "2" "3""#, &[" ".to_string()]); - assert_eq!(args.len_quoted(), 3); + assert_eq!(args.remaining(), 3); assert_eq!(args.single_quoted::().unwrap(), 1); - assert_eq!(args.len_quoted(), 2); + assert_eq!(args.remaining(), 2); assert_eq!(args.single_quoted::().unwrap(), 2); - assert_eq!(args.len_quoted(), 1); + assert_eq!(args.remaining(), 1); assert_eq!(args.single_quoted::().unwrap(), 3); - assert_eq!(args.len_quoted(), 0); + assert_eq!(args.remaining(), 0); } #[test] -fn len_before_and_after_skip() { +fn remaining_len_before_and_after_skip() { let mut args = Args::new("1 2", &[" ".to_string()]); - assert_eq!(args.len(), 2); + assert_eq!(args.remaining(), 2); assert_eq!(args.skip().unwrap(), "1"); - assert_eq!(args.len(), 1); + assert_eq!(args.remaining(), 1); assert_eq!(args.skip().unwrap(), "2"); - assert_eq!(args.len(), 0); + assert_eq!(args.remaining(), 0); } #[test] -fn len_before_and_after_skip_empty_string() { +fn remaining_len_before_and_after_skip_empty_string() { let mut args = Args::new("", &[" ".to_string()]); - assert_eq!(args.len(), 0); + assert_eq!(args.remaining(), 0); assert_eq!(args.skip(), None); - assert_eq!(args.len(), 0); + assert_eq!(args.remaining(), 0); } #[test] -fn len_before_and_after_skip_for() { +fn remaining_len_before_and_after_skip_for() { let mut args = Args::new("1 2", &[" ".to_string()]); - assert_eq!(args.len(), 2); + assert_eq!(args.remaining(), 2); assert_eq!(args.skip_for(2), Some(vec!["1".to_string(), "2".to_string()])); assert_eq!(args.skip_for(2), None); - assert_eq!(args.len(), 0); + assert_eq!(args.remaining(), 0); } #[test] -fn len_before_and_after_find() { +fn remaining_len_before_and_after_find() { let mut args = Args::new("a 2 6", &[" ".to_string()]); - assert_eq!(args.len(), 3); + assert_eq!(args.remaining(), 3); assert_eq!(args.find::().unwrap(), 2); - assert_eq!(args.len(), 2); + assert_eq!(args.remaining(), 2); assert_eq!(args.find::().unwrap(), 6); - assert_eq!(args.len(), 1); + assert_eq!(args.remaining(), 1); assert_eq!(args.find::().unwrap(), "a"); - assert_eq!(args.len(), 0); + assert_eq!(args.remaining(), 0); assert_matches!(args.find::().unwrap_err(), ArgError::Eos); - assert_eq!(args.len(), 0); + assert_eq!(args.remaining(), 0); } #[test] -fn len_before_and_after_find_n() { +fn remaining_len_before_and_after_find_n() { let mut args = Args::new("a 2 6", &[" ".to_string()]); - assert_eq!(args.len(), 3); + assert_eq!(args.remaining(), 3); assert_eq!(args.find_n::().unwrap(), 2); - assert_eq!(args.len(), 3); + assert_eq!(args.remaining(), 3); } @@ -413,15 +416,17 @@ fn single_after_failed_single() { assert_matches!(args.single::().unwrap_err(), ArgError::Parse(_)); // Test that `single` short-circuts on an error and leaves the source as is. - assert_eq!(args.full(), "b 2"); + assert_eq!(args.remaining(), 2); + assert_eq!(args.single::().unwrap(), "b"); + assert_eq!(args.single::().unwrap(), "2"); } #[test] -fn len_quoted_after_failed_single_quoted() { +fn remaining_len_after_failed_single_quoted() { let mut args = Args::new("b a", &[" ".to_string()]); - assert_eq!(args.len_quoted(), 2); + assert_eq!(args.remaining(), 2); // Same goes for `single_quoted` and the alike. assert_matches!(args.single_quoted::().unwrap_err(), ArgError::Parse(_)); - assert_eq!(args.len_quoted(), 2); + assert_eq!(args.remaining(), 2); } From 648ea045b76065eb00b3834043426ca9c6e9e574 Mon Sep 17 00:00:00 2001 From: acdenisSK Date: Sat, 2 Jun 2018 20:45:46 +0200 Subject: [PATCH 04/10] Explain the contradiction on `find`'s behaviour --- src/framework/standard/args.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index 55243ad42d6..aec65dcf18f 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -677,8 +677,9 @@ impl Args { /// Returns the first argument that can be parsed and removes it from the message. The suitable argument /// can be in an arbitrary position in the message. /// - /// **Note**: This replaces all delimiters within the message - /// by the first set in your framework-config to win performance. + /// **Note**: + /// This method contradicts the alternatives for avoiding the catch explained for `Args`, + /// as it actually removes the argument if it was found. /// /// # Examples /// From 8272dd695bd84fb61ada935046a3130e02bedabe Mon Sep 17 00:00:00 2001 From: acdenisSK Date: Sun, 3 Jun 2018 20:00:23 +0200 Subject: [PATCH 05/10] Clarify `len` more --- src/framework/standard/args.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index aec65dcf18f..8e745e317cc 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -359,7 +359,10 @@ impl Args { &s[1..end] } - /// The amount of arguments. + /// The full amount of recognized arguments. + /// + /// **Note**: + /// This never changes. Except for [`find`], which upon success, subtracts the length by 1. (e.g len of `3` becomes `2`) /// /// # Examples /// @@ -370,6 +373,8 @@ impl Args { /// /// assert_eq!(args.len(), 2); // `2` because `["42", "69"]` /// ``` + /// + /// [`find`]: #method.find pub fn len(&self) -> usize { self.args.len() } From 07a9813a8ced575c51b8b1f4b6d2ce1108dbbf20 Mon Sep 17 00:00:00 2001 From: "Alex M. M" Date: Mon, 4 Jun 2018 20:25:30 +0200 Subject: [PATCH 06/10] British English --- src/framework/standard/args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index 8e745e317cc..ab9ddf0131b 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -359,7 +359,7 @@ impl Args { &s[1..end] } - /// The full amount of recognized arguments. + /// The full amount of recognised arguments. /// /// **Note**: /// This never changes. Except for [`find`], which upon success, subtracts the length by 1. (e.g len of `3` becomes `2`) From 31cab12f7726ab74e72d6200d6b5e8c658dbbf05 Mon Sep 17 00:00:00 2001 From: "Alex M. M" Date: Mon, 4 Jun 2018 20:27:06 +0200 Subject: [PATCH 07/10] DOT --- src/framework/standard/args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index ab9ddf0131b..8104641c247 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -417,7 +417,7 @@ impl Args { self.offset >= self.args.len() } - /// Go one step behind + /// Go one step behind. /// /// # Examples /// From 50384a707360a7357a634433b33aa32f609c98c3 Mon Sep 17 00:00:00 2001 From: "Alex M. M" Date: Mon, 4 Jun 2018 20:28:38 +0200 Subject: [PATCH 08/10] APOSTROPHE --- src/framework/standard/args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index 8104641c247..0b086f688d7 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -452,7 +452,7 @@ impl Args { /// /// let mut args = Args::new("42 69 95", &[" ".to_string()]); /// - /// // Lets parse 'em numbers! + /// // Let's parse 'em numbers! /// assert_eq!(args.single::().unwrap(), 42); /// assert_eq!(args.single::().unwrap(), 69); /// assert_eq!(args.single::().unwrap(), 95); From ad99ebec4d641ca2db030ff9a6bd4cd5b44f83c6 Mon Sep 17 00:00:00 2001 From: "Alex M. M" Date: Mon, 4 Jun 2018 20:29:45 +0200 Subject: [PATCH 09/10] CAPITALIZATION --- src/framework/standard/args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index 0b086f688d7..f11f48be8ca 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -459,7 +459,7 @@ impl Args { /// /// // Oh, no! I actually wanted to multiply all of them by 2! /// // I don't want to call `rewind` 3 times manually.... - /// // Wait, i could just go entirely back! + /// // Wait, I could just go entirely back! /// args.restore(); /// /// assert_eq!(args.single::().unwrap() * 2, 84); From 79191dcc32d29650ec6b2d11c38182f9305ae484 Mon Sep 17 00:00:00 2001 From: "Alex M. M" Date: Mon, 4 Jun 2018 20:33:34 +0200 Subject: [PATCH 10/10] AGAIN, CAPITALIZATION --- src/framework/standard/args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index f11f48be8ca..1abe42189e3 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -483,7 +483,7 @@ impl Args { /// /// assert_eq!(args.len_quoted(), 2); // `2` because `["42", "69"]` /// ``` - #[deprecated(since = "0.5.3", note = "its task was merged with `len`, please use it instead.")] + #[deprecated(since = "0.5.3", note = "Its task was merged with `len`, please use it instead.")] pub fn len_quoted(&mut self) -> usize { self.len() }