use std::{
str::FromStr,
error::Error as StdError,
fmt
};
#[derive(Debug)]
pub enum Error<E: StdError> {
Eos,
Parse(E),
}
impl<E: StdError> From<E> for Error<E> {
fn from(e: E) -> Self {
Error::Parse(e)
}
}
impl<E: StdError> StdError for Error<E> {
fn description(&self) -> &str {
use self::Error::*;
match *self {
Eos => "end-of-string",
Parse(ref e) => e.description(),
}
}
fn cause(&self) -> Option<&StdError> {
use self::Error::*;
match *self {
Parse(ref e) => Some(e),
_ => None,
}
}
}
impl<E: StdError> fmt::Display for Error<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match *self {
Eos => write!(f, "end of string"),
Parse(ref e) => fmt::Display::fmt(&e, f),
}
}
}
type Result<T, E> = ::std::result::Result<T, Error<E>>;
fn find_end(s: &str, i: usize) -> Option<usize> {
if i > s.len() {
return None;
}
let mut end = i + 1;
while !s.is_char_boundary(end) {
end += 1;
}
Some(end)
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum TokenKind {
Delimiter,
Argument,
QuotedArgument,
}
#[derive(Debug, Clone)]
struct Token {
kind: TokenKind,
lit: String,
pos: usize,
}
impl Token {
fn new(kind: TokenKind, lit: &str, pos: usize) -> Self {
Token { kind, lit: lit.to_string(), pos }
}
}
#[derive(Debug)]
struct Lexer<'a> {
msg: &'a str,
delims: &'a [char],
offset: usize,
}
impl<'a> Lexer<'a> {
fn new(msg: &'a str, delims: &'a [char]) -> Self {
Lexer {
msg,
delims,
offset: 0,
}
}
#[inline]
fn at_end(&self) -> bool {
self.offset >= self.msg.len()
}
fn current(&self) -> Option<&str> {
if self.at_end() {
return None;
}
let start = self.offset;
let end = find_end(&self.msg, self.offset)?;
Some(&self.msg[start..end])
}
fn next(&mut self) -> Option<()> {
self.offset += self.current()?.len();
Some(())
}
fn commit(&mut self) -> Option<Token> {
if self.at_end() {
return None;
}
if self.current()?.contains(self.delims) {
let start = self.offset;
self.next();
return Some(Token::new(TokenKind::Delimiter, &self.msg[start..self.offset], start));
}
if self.current()? == "\"" {
let start = self.offset;
self.next();
while !self.at_end() && self.current()? != "\"" {
self.next();
}
let is_quote = self.current().map_or(false, |s| s == "\"");
self.next();
let end = self.offset;
return Some(if is_quote {
Token::new(TokenKind::QuotedArgument, &self.msg[start..end], start)
} else {
Token::new(TokenKind::Argument, &self.msg[start..], start)
});
}
let start = self.offset;
while !self.at_end() {
if self.current()?.contains(self.delims) {
break;
}
self.next();
}
Some(Token::new(TokenKind::Argument, &self.msg[start..self.offset], start))
}
}
#[derive(Clone, Debug)]
pub struct Args {
message: String,
args: Vec<Token>,
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::<Vec<_>>();
let mut args = Vec::new();
if delims.is_empty() && !message.is_empty() {
args.push(Token::new(TokenKind::Argument, &message[..], 0));
} else {
let mut lex = Lexer::new(message, &delims);
while let Some(token) = lex.commit() {
if token.kind == TokenKind::Delimiter {
continue;
}
args.push(token);
}
}
Args {
args,
message: message.to_string(),
offset: 0,
}
}
pub fn current(&self) -> Option<&str> {
self.args.get(self.offset).map(|t| t.lit.as_str())
}
pub fn single<T: FromStr>(&mut self) -> Result<T, T::Err>
where T::Err: StdError {
if self.is_empty() {
return Err(Error::Eos);
}
let cur = &self.args[self.offset];
let parsed = T::from_str(&cur.lit)?;
self.offset += 1;
Ok(parsed)
}
pub fn single_n<T: FromStr>(&self) -> Result<T, T::Err>
where T::Err: StdError {
if self.is_empty() {
return Err(Error::Eos);
}
let cur = &self.args[self.offset];
Ok(T::from_str(&cur.lit)?)
}
pub fn skip(&mut self) -> Option<String> {
if self.is_empty() {
return None;
}
self.single::<String>().ok()
}
pub fn skip_for(&mut self, i: u32) -> Option<Vec<String>> {
if self.is_empty() {
return None;
}
let mut vec = Vec::with_capacity(i as usize);
for _ in 0..i {
vec.push(self.skip()?);
}
Some(vec)
}
pub fn iter<T: FromStr>(&mut self) -> Iter<T>
where T::Err: StdError {
Iter::new(self)
}
pub fn multiple<T: FromStr>(mut self) -> Result<Vec<T>, T::Err>
where T::Err: StdError {
if self.is_empty() {
return Err(Error::Eos);
}
self.iter::<T>().collect()
}
pub fn current_quoted(&self) -> Option<&str> {
self.args.get(self.offset).map(|t| quotes_extract(t))
}
pub fn single_quoted<T: FromStr>(&mut self) -> Result<T, T::Err>
where T::Err: StdError {
if self.is_empty() {
return Err(Error::Eos);
}
let current = &self.args[self.offset];
let lit = quotes_extract(current);
let parsed = T::from_str(&lit)?;
self.offset += 1;
Ok(parsed)
}
pub fn single_quoted_n<T: FromStr>(&self) -> Result<T, T::Err>
where T::Err: StdError {
if self.is_empty() {
return Err(Error::Eos);
}
let current = &self.args[self.offset];
let lit = quotes_extract(current);
Ok(T::from_str(&lit)?)
}
pub fn iter_quoted<T: FromStr>(&mut self) -> IterQuoted<T>
where T::Err: StdError {
IterQuoted::new(self)
}
pub fn multiple_quoted<T: FromStr>(mut self) -> Result<Vec<T>, T::Err>
where T::Err: StdError {
if self.is_empty() {
return Err(Error::Eos);
}
self.iter_quoted::<T>().collect()
}
pub fn find<T: FromStr>(&mut self) -> Result<T, T::Err>
where T::Err: StdError {
if self.is_empty() {
return Err(Error::Eos);
}
let pos = match self.args.iter().map(|t| quotes_extract(t)).position(|s| s.parse::<T>().is_ok()) {
Some(p) => p,
None => return Err(Error::Eos),
};
let parsed = T::from_str(quotes_extract(&self.args[pos]))?;
self.args.remove(pos);
self.rewind();
Ok(parsed)
}
pub fn find_n<T: FromStr>(&mut self) -> Result<T, T::Err>
where T::Err: StdError {
if self.is_empty() {
return Err(Error::Eos);
}
let pos = match self.args.iter().map(|t| quotes_extract(t)).position(|s| s.parse::<T>().is_ok()) {
Some(p) => p,
None => return Err(Error::Eos),
};
Ok(T::from_str(quotes_extract(&self.args[pos]))?)
}
pub fn full(&self) -> &str {
&self.message
}
pub fn full_quoted(&self) -> &str {
let s = &self.message;
if !s.starts_with('"') {
return s;
}
let end = s.rfind('"').unwrap();
if end == 0 {
return s;
}
&s[1..end]
}
pub fn rest(&self) -> &str {
if self.is_empty() {
return "";
}
let args = &self.args[self.offset..];
if let Some(token) = args.get(0) {
&self.message[token.pos..]
} else {
&self.message[..]
}
}
pub fn len(&self) -> usize {
self.args.len()
}
pub fn is_empty(&self) -> bool {
self.offset >= self.args.len()
}
pub fn remaining(&self) -> usize {
if self.is_empty() {
return 0;
}
self.len() - self.offset
}
#[inline]
pub fn next(&mut self) {
self.offset += 1;
}
#[inline]
pub fn rewind(&mut self) {
if self.offset == 0 {
return;
}
self.offset -= 1;
}
#[inline]
pub fn restore(&mut self) {
self.offset = 0;
}
#[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()
}
}
impl ::std::ops::Deref for Args {
type Target = str;
fn deref(&self) -> &Self::Target { self.full() }
}
impl PartialEq<str> for Args {
fn eq(&self, other: &str) -> bool {
self.message == other
}
}
impl<'a> PartialEq<&'a str> for Args {
fn eq(&self, other: &&'a str) -> bool {
self.message == *other
}
}
impl PartialEq for Args {
fn eq(&self, other: &Self) -> bool {
self.message == *other.message
}
}
impl Eq for Args {}
use std::marker::PhantomData;
pub struct Iter<'a, T: FromStr> where T::Err: StdError {
args: &'a mut Args,
_marker: PhantomData<T>,
}
impl<'a, T: FromStr> Iter<'a, T> where T::Err: StdError {
fn new(args: &'a mut Args) -> Self {
Iter { args, _marker: PhantomData }
}
}
impl<'a, T: FromStr> Iterator for Iter<'a, T> where T::Err: StdError {
type Item = Result<T, T::Err>;
fn next(&mut self) -> Option<Self::Item> {
if self.args.is_empty() {
None
} else {
Some(self.args.single::<T>())
}
}
}
pub struct IterQuoted<'a, T: FromStr> where T::Err: StdError {
args: &'a mut Args,
_marker: PhantomData<T>,
}
impl<'a, T: FromStr> IterQuoted<'a, T> where T::Err: StdError {
fn new(args: &'a mut Args) -> Self {
IterQuoted { args, _marker: PhantomData }
}
}
impl<'a, T: FromStr> Iterator for IterQuoted<'a, T> where T::Err: StdError {
type Item = Result<T, T::Err>;
fn next(&mut self) -> Option<Self::Item> {
if self.args.is_empty() {
None
} else {
Some(self.args.single_quoted::<T>())
}
}
}
fn quotes_extract(token: &Token) -> &str {
if token.kind == TokenKind::QuotedArgument {
&token.lit[1..token.lit.len() - 1]
} else {
&token.lit
}
}
#[cfg(test)]
mod test {
use super::{Args, Error as ArgError};
#[test]
fn single_with_empty_message() {
let mut args = Args::new("", &["".to_string()]);
assert_matches!(args.single::<String>().unwrap_err(), ArgError::Eos);
let mut args = Args::new("", &[",".to_string()]);
assert_matches!(args.single::<String>().unwrap_err(), ArgError::Eos);
}
#[test]
fn single_n_with_empty_message() {
let args = Args::new("", &["".to_string()]);
assert_matches!(args.single_n::<String>().unwrap_err(), ArgError::Eos);
let args = Args::new("", &[",".to_string()]);
assert_matches!(args.single_n::<String>().unwrap_err(), ArgError::Eos);
}
#[test]
fn single_quoted_with_empty_message() {
let mut args = Args::new("", &["".to_string()]);
assert_matches!(args.single_quoted::<String>().unwrap_err(), ArgError::Eos);
let mut args = Args::new("", &[",".to_string()]);
assert_matches!(args.single_quoted::<String>().unwrap_err(), ArgError::Eos);
}
#[test]
fn multiple_with_empty_message() {
let args = Args::new("", &["".to_string()]);
assert_matches!(args.multiple::<String>().unwrap_err(), ArgError::Eos);
let args = Args::new("", &[",".to_string()]);
assert_matches!(args.multiple::<String>().unwrap_err(), ArgError::Eos);
}
#[test]
fn multiple_quoted_with_empty_message() {
let args = Args::new("", &["".to_string()]);
assert_matches!(args.multiple_quoted::<String>().unwrap_err(), ArgError::Eos);
let args = Args::new("", &[",".to_string()]);
assert_matches!(args.multiple_quoted::<String>().unwrap_err(), ArgError::Eos);
}
#[test]
fn skip_with_empty_message() {
let mut args = Args::new("", &["".to_string()]);
assert_matches!(args.skip(), None);
let mut args = Args::new("", &[",".to_string()]);
assert_matches!(args.skip(), None);
}
#[test]
fn skip_for_with_empty_message() {
let mut args = Args::new("", &["".to_string()]);
assert_matches!(args.skip_for(0), None);
let mut args = Args::new("", &["".to_string()]);
assert_matches!(args.skip_for(5), None);
let mut args = Args::new("", &[",".to_string()]);
assert_matches!(args.skip_for(0), None);
let mut args = Args::new("", &[",".to_string()]);
assert_matches!(args.skip_for(5), None);
}
#[test]
fn single_i32_with_2_bytes_long_delimiter() {
let mut args = Args::new("1, 2", &[", ".to_string()]);
assert_eq!(args.single::<i32>().unwrap(), 1);
assert_eq!(args.single::<i32>().unwrap(), 2);
}
#[test]
fn single_i32_with_1_byte_long_delimiter_i32() {
let mut args = Args::new("1,2", &[",".to_string()]);
assert_eq!(args.single::<i32>().unwrap(), 1);
assert_eq!(args.single::<i32>().unwrap(), 2);
}
#[test]
fn single_i32_with_wrong_char_after_first_arg() {
let mut args = Args::new("1, 2", &[",".to_string()]);
assert_eq!(args.single::<i32>().unwrap(), 1);
assert!(args.single::<i32>().is_err());
}
#[test]
fn single_i32_with_one_character_being_3_bytes_long() {
let mut args = Args::new("1★2", &["★".to_string()]);
assert_eq!(args.single::<i32>().unwrap(), 1);
assert_eq!(args.single::<i32>().unwrap(), 2);
}
#[test]
fn single_i32_with_untrimmed_whitespaces() {
let mut args = Args::new(" 1, 2 ", &[",".to_string()]);
assert!(args.single::<i32>().is_err());
}
#[test]
fn single_i32_n() {
let args = Args::new("1,2", &[",".to_string()]);
assert_eq!(args.single_n::<i32>().unwrap(), 1);
assert_eq!(args.single_n::<i32>().unwrap(), 1);
}
#[test]
fn single_quoted_chaining() {
let mut args = Args::new(r#""1, 2" "2" """#, &[" ".to_string()]);
assert_eq!(args.single_quoted::<String>().unwrap(), "1, 2");
assert_eq!(args.single_quoted::<String>().unwrap(), "2");
assert_eq!(args.single_quoted::<String>().unwrap(), "");
}
#[test]
fn single_quoted_and_single_chaining() {
let mut args = Args::new(r#""1, 2" "2" "3" 4"#, &[" ".to_string()]);
assert_eq!(args.single_quoted::<String>().unwrap(), "1, 2");
assert!(args.single_n::<i32>().is_err());
assert_eq!(args.single::<String>().unwrap(), "\"2\"");
assert_eq!(args.single_quoted::<i32>().unwrap(), 3);
assert_eq!(args.single::<i32>().unwrap(), 4);
}
#[test]
fn full_on_args() {
let test_text = "Some text to ensure `full()` works.";
let args = Args::new(test_text, &[" ".to_string()]);
assert_eq!(args.full(), test_text);
}
#[test]
fn multiple_quoted_strings_one_delimiter() {
let args = Args::new(r#""1, 2" "a" "3" 4 "5"#, &[" ".to_string()]);
assert_eq!(args.multiple_quoted::<String>().unwrap(), ["1, 2", "a", "3", "4", "\"5"]);
}
#[test]
fn multiple_quoted_strings_with_multiple_delimiter() {
let args = Args::new(r#""1, 2" "a","3"4 "5"#, &[" ".to_string(), ",".to_string()]);
assert_eq!(args.multiple_quoted::<String>().unwrap(), ["1, 2", "a", "3", "4", "\"5"]);
}
#[test]
fn multiple_quoted_strings_with_multiple_delimiters() {
let args = Args::new(r#""1, 2" "a","3" """#, &[" ".to_string(), ",".to_string()]);
assert_eq!(args.multiple_quoted::<String>().unwrap(), ["1, 2", "a", "3", ""]);
}
#[test]
fn multiple_quoted_i32() {
let args = Args::new(r#""1" "2" 3"#, &[" ".to_string()]);
assert_eq!(args.multiple_quoted::<i32>().unwrap(), [1, 2, 3]);
}
#[test]
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::<String>().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::<String>().unwrap(), ["hello", "\"2 b"]);
}
#[test]
fn multiple_quoted_one_quote_pair() {
let args = Args::new(r#"hello "2 b""#, &[",".to_string(), " ".to_string()]);
assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello", "2 b"]);
}
#[test]
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::<String>().unwrap(), ["hello, my name is cake", "2"]);
}
#[test]
fn no_quote() {
let args = Args::new("hello, my name is cake", &[",".to_string(), " ".to_string()]);
assert_eq!(args.single_quoted_n::<String>().unwrap(), "hello");
}
#[test]
fn single_quoted_n() {
let args = Args::new(r#""hello, my name is cake","test"#, &[",".to_string()]);
assert_eq!(args.single_quoted_n::<String>().unwrap(), "hello, my name is cake");
assert_eq!(args.single_quoted_n::<String>().unwrap(), "hello, my name is cake");
}
#[test]
fn multiple_quoted_starting_with_wrong_delimiter_in_first_quote() {
let args = Args::new(r#""hello, my name is cake" "2""#, &[",".to_string(), " ".to_string()]);
assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello, my name is cake", "2"]);
}
#[test]
fn multiple_quoted_with_one_correct_and_one_invalid_quote() {
let args = Args::new(r#""hello, my name is cake" "2""#, &[",".to_string(), " ".to_string()]);
assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello, my name is cake", "2"]);
}
#[test]
fn find_i32_one_one_byte_delimiter() {
let mut args = Args::new("hello,my name is cake 2", &[" ".to_string()]);
assert_eq!(args.find::<i32>().unwrap(), 2);
}
#[test]
fn find_i32_one_three_byte_delimiter() {
let mut args = Args::new("hello,my name is cakeé2", &["é".to_string()]);
assert_eq!(args.find::<i32>().unwrap(), 2);
}
#[test]
fn find_i32_multiple_delimiter_but_i32_not_last() {
let mut args = Args::new("hello,my name is 2 cake", &[" ".to_string(), ",".to_string()]);
assert_eq!(args.find::<i32>().unwrap(), 2);
}
#[test]
fn find_i32_multiple_delimiter() {
let mut args = Args::new("hello,my name is cake 2", &[" ".to_string(), ",".to_string()]);
assert_eq!(args.find::<i32>().unwrap(), 2);
}
#[test]
fn find_n_i32() {
let mut args = Args::new("a 2", &[" ".to_string()]);
assert_eq!(args.find_n::<i32>().unwrap(), 2);
assert_eq!(args.find_n::<i32>().unwrap(), 2);
}
#[test]
fn skip() {
let mut args = Args::new("1 2", &[" ".to_string()]);
assert_eq!(args.skip().unwrap(), "1");
assert_eq!(args.remaining(), 1);
assert_eq!(args.single::<String>().unwrap(), "2");
}
#[test]
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.remaining(), 2);
assert_eq!(args.single::<String>().unwrap(), "neko");
assert_eq!(args.single::<String>().unwrap(), "100");
}
#[test]
fn len_with_one_delimiter() {
let args = Args::new("1 2 neko 100", &[" ".to_string()]);
assert_eq!(args.len(), 4);
assert_eq!(args.remaining(), 4);
}
#[test]
fn len_multiple_quoted() {
let args = Args::new(r#""hello, my name is cake" "2""#, &[" ".to_string()]);
assert_eq!(args.len(), 2);
}
#[test]
fn remaining_len_before_and_after_single() {
let mut args = Args::new("1 2", &[" ".to_string()]);
assert_eq!(args.remaining(), 2);
assert_eq!(args.single::<i32>().unwrap(), 1);
assert_eq!(args.remaining(), 1);
assert_eq!(args.single::<i32>().unwrap(), 2);
assert_eq!(args.remaining(), 0);
}
#[test]
fn remaining_len_before_and_after_single_quoted() {
let mut args = Args::new(r#""1" "2" "3""#, &[" ".to_string()]);
assert_eq!(args.remaining(), 3);
assert_eq!(args.single_quoted::<i32>().unwrap(), 1);
assert_eq!(args.remaining(), 2);
assert_eq!(args.single_quoted::<i32>().unwrap(), 2);
assert_eq!(args.remaining(), 1);
assert_eq!(args.single_quoted::<i32>().unwrap(), 3);
assert_eq!(args.remaining(), 0);
}
#[test]
fn remaining_len_before_and_after_skip() {
let mut args = Args::new("1 2", &[" ".to_string()]);
assert_eq!(args.remaining(), 2);
assert_eq!(args.skip().unwrap(), "1");
assert_eq!(args.remaining(), 1);
assert_eq!(args.skip().unwrap(), "2");
assert_eq!(args.remaining(), 0);
}
#[test]
fn remaining_len_before_and_after_skip_empty_string() {
let mut args = Args::new("", &[" ".to_string()]);
assert_eq!(args.remaining(), 0);
assert_eq!(args.skip(), None);
assert_eq!(args.remaining(), 0);
}
#[test]
fn remaining_len_before_and_after_skip_for() {
let mut args = Args::new("1 2", &[" ".to_string()]);
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.remaining(), 0);
}
#[test]
fn remaining_len_before_and_after_find() {
let mut args = Args::new("a 2 6", &[" ".to_string()]);
assert_eq!(args.remaining(), 3);
assert_eq!(args.find::<i32>().unwrap(), 2);
assert_eq!(args.remaining(), 2);
assert_eq!(args.find::<i32>().unwrap(), 6);
assert_eq!(args.remaining(), 1);
assert_eq!(args.find::<String>().unwrap(), "a");
assert_eq!(args.remaining(), 0);
assert_matches!(args.find::<String>().unwrap_err(), ArgError::Eos);
assert_eq!(args.remaining(), 0);
}
#[test]
fn remaining_len_before_and_after_find_n() {
let mut args = Args::new("a 2 6", &[" ".to_string()]);
assert_eq!(args.remaining(), 3);
assert_eq!(args.find_n::<i32>().unwrap(), 2);
assert_eq!(args.remaining(), 3);
}
#[test]
fn multiple_strings_with_one_delimiter() {
let args = Args::new("hello, my name is cake 2", &[" ".to_string()]);
assert_eq!(args.multiple::<String>().unwrap(), ["hello,", "my", "name", "is", "cake", "2"]);
}
#[test]
fn multiple_i32_with_one_delimiter() {
let args = Args::new("1 2 3", &[" ".to_string()]);
assert_eq!(args.multiple::<i32>().unwrap(), [1, 2, 3]);
}
#[test]
fn multiple_i32_with_one_delimiter_and_parse_error() {
let args = Args::new("1 2 3 abc", &[" ".to_string()]);
assert_matches!(args.multiple::<i32>().unwrap_err(), ArgError::Parse(_));
}
#[test]
fn multiple_i32_with_three_delimiters() {
let args = Args::new("1 2 3", &[" ".to_string(), ",".to_string()]);
assert_eq!(args.multiple::<i32>().unwrap(), [1, 2, 3]);
}
#[test]
fn single_after_failed_single() {
let mut args = Args::new("b 2", &[" ".to_string()]);
assert_matches!(args.single::<i32>().unwrap_err(), ArgError::Parse(_));
assert_eq!(args.remaining(), 2);
assert_eq!(args.single::<String>().unwrap(), "b");
assert_eq!(args.single::<String>().unwrap(), "2");
}
#[test]
fn remaining_len_after_failed_single_quoted() {
let mut args = Args::new("b a", &[" ".to_string()]);
assert_eq!(args.remaining(), 2);
assert_matches!(args.single_quoted::<i32>().unwrap_err(), ArgError::Parse(_));
assert_eq!(args.remaining(), 2);
}
#[test]
fn no_delims_entire_message() {
let mut args = Args::new("abc", &[]);
assert_eq!(args.remaining(), 1);
assert_eq!(args.single::<String>().unwrap(), "abc");
assert_eq!(args.remaining(), 0);
}
}