Skip to content

Commit

Permalink
Add "zero-copy" parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
arqunis committed Oct 21, 2017
1 parent fbd6258 commit 9428787
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 1 deletion.
82 changes: 82 additions & 0 deletions src/framework/standard/args.rs
Expand Up @@ -101,6 +101,36 @@ impl Args {
.parse::<T>()?)
}

/// Like [`single`], but does "zero-copy" parsing.
///
/// Refer to [`FromStrZc`]'s example on how to use this method.
///
/// [`single`]: #method.single
/// [`FromStrZc`]: trait.FromStrZc.html
pub fn single_zc<'a, T: FromStrZc<'a> + 'a>(&'a mut self) -> Result<T, T::Err>
where T::Err: StdError {

// This is a hack as to mitigate some nasty lifetime errors.
//
// (Culprit `Vec::remove`s return type)
fn get_and_remove(b: &mut Vec<String>) -> Option<&str> {
struct GetThenRemove<'a>(&'a mut Vec<String>);

impl<'a> Drop for GetThenRemove<'a> {
fn drop(&mut self) {
if !self.0.is_empty() {
self.0.remove(0);
}
}
}

GetThenRemove(b).0.get(0).map(|s| s.as_str())
}

let a = get_and_remove(&mut self.delimiter_split).ok_or(Error::Eos)?;
Ok(FromStrZc::from_str(a)?)
}

/// Like [`single`], but doesn't remove the element.
///
/// # Examples
Expand Down Expand Up @@ -317,6 +347,58 @@ impl Args {
}
}

/// A version of `FromStr` that allows for "zero-copy" parsing.
///
/// # Examples
///
/// ```rust,ignore
/// use serenity::framework::standard::{Args, FromStrZc};
/// use std::fmt;
///
/// struct NameDiscrim<'a>(&'a str, Option<&'a str>);
///
/// #[derive(Debug)]
/// struct Error(&'static str);
///
/// impl std::error::Error for Error {
/// fn description(&self) -> &str { self.0 }
/// }
///
/// impl fmt::Display for Error {
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) }
/// }
///
/// impl<'a> FromStrZc<'a> for NameDiscrim<'a> {
/// type Err = Error;
///
/// fn from_str(s: &'a str) -> Result<NameDiscrim<'a>, Error> {
/// let mut it = s.split("#");
/// let name = it.next().ok_or(Error("name must be specified"))?;
/// let discrim = it.next();
/// Ok(NameDiscrim(name, discrim))
/// }
/// }
///
/// let mut args = Args::new("abc#1234", " ");
/// let NameDiscrim(name, discrim) = args.single_zc::<NameDiscrim>().unwrap();
///
/// assert_eq!(name, "abc");
/// assert_eq!(discrim, Some("1234"));
/// ```
pub trait FromStrZc<'a>: Sized {
type Err;

fn from_str(s: &'a str) -> ::std::result::Result<Self, Self::Err>;
}

impl<'a, T: FromStr> FromStrZc<'a> for T {
type Err = T::Err;

fn from_str(s: &'a str) -> ::std::result::Result<Self, Self::Err> {
<T as FromStr>::from_str(s)
}
}

impl ::std::ops::Deref for Args {
type Target = [String];

Expand Down
2 changes: 1 addition & 1 deletion src/framework/standard/mod.rs
Expand Up @@ -14,7 +14,7 @@ pub use self::command::CommandOrAlias;
pub use self::configuration::Configuration;
pub use self::create_command::CreateCommand;
pub use self::create_group::CreateGroup;
pub use self::args::{Args, Error as ArgError};
pub use self::args::{Args, Iter, FromStrZc, Error as ArgError};

use self::command::{AfterHook, BeforeHook};
use std::collections::HashMap;
Expand Down

0 comments on commit 9428787

Please sign in to comment.