aboutsummaryrefslogtreecommitdiff
path: root/src/framework
diff options
context:
space:
mode:
authoracdenisSK <[email protected]>2017-10-24 18:10:10 +0200
committeracdenisSK <[email protected]>2017-10-24 18:10:10 +0200
commitef60c3cd5b93d61ff8200f5f6871b449bf7dccb5 (patch)
treeff9ec9e09dc363c05c6b582380a120ca47290a9f /src/framework
parentRemove `on_` prefix to EventHandler tymethods (diff)
parentFall back to `str::parse` if `parse_username` fails (diff)
downloadserenity-ef60c3cd5b93d61ff8200f5f6871b449bf7dccb5.tar.xz
serenity-ef60c3cd5b93d61ff8200f5f6871b449bf7dccb5.zip
Merge v0.4.2
Diffstat (limited to 'src/framework')
-rw-r--r--src/framework/standard/args.rs238
-rw-r--r--src/framework/standard/help_commands.rs33
-rw-r--r--src/framework/standard/mod.rs27
3 files changed, 277 insertions, 21 deletions
diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs
index f59908a..6186cd4 100644
--- a/src/framework/standard/args.rs
+++ b/src/framework/standard/args.rs
@@ -83,6 +83,17 @@ impl Args {
}
/// Removes the first element, parses it to a specific type if necessary, returns.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new("42 69", " ");
+ ///
+ /// assert_eq!(args.single::<i32>().unwrap(), 42);
+ /// assert_eq!(args, ["69"]);
+ /// ```
pub fn single<T: FromStr>(&mut self) -> Result<T, T::Err>
where T::Err: StdError {
if self.delimiter_split.is_empty() {
@@ -95,8 +106,49 @@ 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
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let args = Args::new("42 69", " ");
+ ///
+ /// assert_eq!(args.single_n::<i32>().unwrap(), 42);
+ /// assert_eq!(args, ["42", "69"]);
+ /// ```
+ ///
/// [`single`]: #method.single
pub fn single_n<T: FromStr>(&self) -> Result<T, T::Err>
where T::Err: StdError {
@@ -111,10 +163,31 @@ impl Args {
}
/// Skips if there's a first element, but also returns it.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new("42 69", " ");
+ ///
+ /// assert_eq!(args.skip().unwrap(), "42");
+ /// assert_eq!(args, ["69"]);
+ /// ```
pub fn skip(&mut self) -> Option<String> { self.delimiter_split.shift() }
/// Like [`skip`], but allows for multiple at once.
///
+ /// # Examples
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new("42 69 88 99", " ");
+ ///
+ /// assert_eq!(*args.skip_for(3).unwrap(), ["42".to_string(), "69".to_string(), "88".to_string()]);
+ /// assert_eq!(args, ["99"]);
+ /// ```
+ ///
/// [`skip`]: #method.skip
pub fn skip_for(&mut self, i: u32) -> Option<Vec<String>> {
let mut vec = Vec::with_capacity(i as usize);
@@ -127,7 +200,18 @@ impl Args {
}
/// Like [`single`], but takes quotes into account.
- ///
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new(r#""42"#, " ");
+ ///
+ /// assert_eq!(args.single_quoted::<i32>().unwrap(), 42);
+ /// assert!(args.is_empty());
+ /// ```
+ ///
/// [`single`]: #method.single
pub fn single_quoted<T: FromStr>(&mut self) -> Result<T, T::Err>
where T::Err: StdError {
@@ -162,20 +246,62 @@ impl Args {
}
/// Empty outs the internal vector while parsing (if necessary) and returning them
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new("42 69", " ");
+ ///
+ /// assert_eq!(*args.list::<i32>().unwrap(), [42, 69]);
+ /// ```
pub fn list<T: FromStr>(mut self) -> Result<Vec<T>, T::Err>
where T::Err: StdError {
Iter::<T>::new(&mut self).collect()
}
/// Provides an iterator of items: (`T: FromStr`) `Result<T, T::Err>`.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new("3 4", " ");
+ ///
+ /// assert_eq!(*args.iter::<i32>().map(|num| num.unwrap().pow(2)).collect::<Vec<_>>(), [9, 16]);
+ /// assert!(args.is_empty());
+ /// ```
pub fn iter<T: FromStr>(&mut self) -> Iter<T> where T::Err: StdError {
Iter::new(self)
}
/// This method is just `internal_vector.join(delimiter)`
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new("42 69", " ");
+ ///
+ /// assert_eq!(args.full(), "42 69");
+ /// ```
pub fn full(&self) -> String { self.delimiter_split.join(&self.delimiter) }
/// Returns the first argument that can be converted and removes it from the list.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new("c47 69", " ");
+ ///
+ /// assert_eq!(args.find::<i32>().unwrap(), 69);
+ /// assert_eq!(args, ["c47"]);
+ /// ```
pub fn find<T: FromStr>(&mut self) -> Result<T, T::Err>
where T::Err: StdError {
if self.delimiter_split.is_empty() {
@@ -201,6 +327,17 @@ impl Args {
}
/// Returns the first argument that can be converted and does not remove it from the list.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let args = Args::new("c47 69", " ");
+ ///
+ /// assert_eq!(args.find_n::<i32>().unwrap(), 69);
+ /// assert_eq!(args, ["c47", "69"]);
+ /// ```
pub fn find_n<T: FromStr>(&self) -> Result<T, T::Err>
where T::Err: StdError {
if self.delimiter_split.is_empty() {
@@ -215,12 +352,111 @@ 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];
fn deref(&self) -> &Self::Target { &self.delimiter_split }
}
+impl<'a> PartialEq<[&'a str]> for Args {
+ fn eq(&self, other: &[&str]) -> bool {
+ let mut b = true;
+
+ for (s, o) in self.delimiter_split.iter().zip(other.iter()) {
+ if s != o {
+ b = false;
+ break;
+ }
+ }
+
+ b
+ }
+}
+
+macro_rules! impl_slices {
+ ($($num:expr),*) => {
+ impl<'a> PartialEq<[&'a str; 0]> for Args {
+ fn eq(&self, _: &[&str; 0]) -> bool {
+ self.delimiter_split.is_empty()
+ }
+ }
+
+ $(
+ impl<'a> PartialEq<[&'a str; $num]> for Args {
+ fn eq(&self, other: &[&str; $num]) -> bool {
+ <Args as PartialEq<[&str]>>::eq(self, &other[..])
+ }
+ }
+ )*
+ }
+}
+
+impl_slices! {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32
+}
+
+impl PartialEq for Args {
+ fn eq(&self, other: &Self) -> bool {
+ self.delimiter_split == other.delimiter_split
+ }
+}
+
+impl Eq for Args {}
+
use std::marker::PhantomData;
/// Provides `list`'s functionality, but as an iterator.
diff --git a/src/framework/standard/help_commands.rs b/src/framework/standard/help_commands.rs
index ce3e406..ef7732b 100644
--- a/src/framework/standard/help_commands.rs
+++ b/src/framework/standard/help_commands.rs
@@ -62,9 +62,9 @@ pub fn has_all_requirements(cmd: &Command, msg: &Message) -> bool {
if let Ok(permissions) = member.permissions() {
if cmd.allowed_roles.is_empty() {
- return permissions.administrator() || has_correct_permissions(&cmd, &msg);
+ return permissions.administrator() || has_correct_permissions(cmd, msg);
} else {
- return permissions.administrator() || (has_correct_roles(&cmd, &guild, &member) && has_correct_permissions(cmd, msg));
+ return permissions.administrator() || (has_correct_roles(cmd, &guild, member) && has_correct_permissions(cmd, msg));
}
}
}
@@ -111,22 +111,20 @@ pub fn with_embeds(_: &mut Context,
if name == with_prefix || name == *command_name {
match *command {
CommandOrAlias::Command(ref cmd) => {
- if has_all_requirements(&cmd, &msg) {
+ if has_all_requirements(cmd, msg) {
found = Some((command_name, cmd));
- }
- else {
+ } else {
break;
}
},
CommandOrAlias::Alias(ref name) => {
- let actual_command = group.commands.get(name).unwrap();
+ let actual_command = &group.commands[name];
match *actual_command {
CommandOrAlias::Command(ref cmd) => {
- if has_all_requirements(&cmd, &msg) {
+ if has_all_requirements(cmd, msg) {
found = Some((name, cmd));
- }
- else {
+ } else {
break;
}
},
@@ -229,12 +227,9 @@ pub fn with_embeds(_: &mut Context,
for name in command_names {
let cmd = &commands[name];
- if cmd.help_available {
-
- if cmd.help_available && has_all_requirements(&cmd, &msg) {
- let _ = write!(desc, "`{}`\n", name);
- has_commands = true;
- }
+ if cmd.help_available && has_all_requirements(cmd, msg) {
+ let _ = write!(desc, "`{}`\n", name);
+ has_commands = true;
}
}
@@ -288,7 +283,7 @@ pub fn plain(_: &mut Context,
if name == with_prefix || name == *command_name {
match *command {
CommandOrAlias::Command(ref cmd) => {
- if has_all_requirements(&cmd, &msg) {
+ if has_all_requirements(cmd, msg) {
found = Some((command_name, cmd));
}
else {
@@ -296,11 +291,11 @@ pub fn plain(_: &mut Context,
}
},
CommandOrAlias::Alias(ref name) => {
- let actual_command = group.commands.get(name).unwrap();
+ let actual_command = &group.commands[name];
match *actual_command {
CommandOrAlias::Command(ref cmd) => {
- if has_all_requirements(&cmd, &msg) {
+ if has_all_requirements(cmd, msg) {
found = Some((name, cmd));
}
else {
@@ -389,7 +384,7 @@ pub fn plain(_: &mut Context,
for name in command_names {
let cmd = &commands[name];
- if cmd.help_available && has_all_requirements(&cmd, &msg) {
+ if cmd.help_available && has_all_requirements(cmd, msg) {
let _ = write!(group_help, "`{}` ", name);
}
}
diff --git a/src/framework/standard/mod.rs b/src/framework/standard/mod.rs
index 6504c6a..36570af 100644
--- a/src/framework/standard/mod.rs
+++ b/src/framework/standard/mod.rs
@@ -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;
@@ -138,6 +138,31 @@ pub enum DispatchError {
WebhookAuthor,
}
+use std::fmt;
+
+impl fmt::Debug for DispatchError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::DispatchError::*;
+
+ match *self {
+ CheckFailed(..) => write!(f, "DispatchError::CheckFailed"),
+ CommandDisabled(ref s) => f.debug_tuple("DispatchError::CommandDisabled").field(&s).finish(),
+ BlockedUser => write!(f, "DispatchError::BlockedUser"),
+ BlockedGuild => write!(f, "DispatchError::BlockedGuild"),
+ LackOfPermissions(ref perms) => f.debug_tuple("DispatchError::LackOfPermissions").field(&perms).finish(),
+ RateLimited(ref num) => f.debug_tuple("DispatchError::RateLimited").field(&num).finish(),
+ OnlyForDM => write!(f, "DispatchError::OnlyForDM"),
+ OnlyForOwners => write!(f, "DispatchError::OnlyForOwners"),
+ OnlyForGuilds => write!(f, "DispatchError::OnlyForGuilds"),
+ LackingRole => write!(f, "DispatchError::LackingRole"),
+ NotEnoughArguments { ref min, ref given } => f.debug_struct("DispatchError::NotEnoughArguments").field("min", &min).field("given", &given).finish(),
+ TooManyArguments { ref max, ref given } => f.debug_struct("DispatchError::TooManyArguments").field("max", &max).field("given", &given).finish(),
+ IgnoredBot => write!(f, "DispatchError::IgnoredBot"),
+ WebhookAuthor => write!(f, "DispatchError::WebhookAuthor"),
+ }
+ }
+}
+
type DispatchErrorHook = Fn(Context, Message, DispatchError) + Send + Sync + 'static;
/// A utility for easily managing dispatches to commands.