From f9e5e76585a1f6317dadb67e440765b0070ca131 Mon Sep 17 00:00:00 2001 From: acdenisSK Date: Wed, 18 Oct 2017 20:56:07 +0200 Subject: Add `PartialEq` impls and doc tests to `Args` --- src/framework/standard/args.rs | 155 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 1 deletion(-) (limited to 'src/framework') diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index 7e3d579..8190dd5 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -78,6 +78,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::().unwrap(), 42); + /// assert_eq!(args, ["69"]); + /// ``` pub fn single(&mut self) -> Result where T::Err: StdError { if self.delimiter_split.is_empty() { @@ -92,6 +103,17 @@ impl Args { /// 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::().unwrap(), 42); + /// assert!(args == ["42", "69"]); + /// ``` + /// /// [`single`]: #method.single pub fn single_n(&self) -> Result where T::Err: StdError { @@ -106,10 +128,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 { 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> { let mut vec = Vec::with_capacity(i as usize); @@ -122,7 +165,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::().unwrap(), 42); + /// assert!(args.is_empty()); + /// ``` + /// /// [`single`]: #method.single pub fn single_quoted(&mut self) -> Result where T::Err: StdError { @@ -157,20 +211,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::().unwrap(), [42, 69]); + /// ``` pub fn list(mut self) -> Result, T::Err> where T::Err: StdError { Iter::::new(&mut self).collect() } /// Provides an iterator of items: (`T: FromStr`) `Result`. + /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let mut args = Args::new("3 4", " "); + /// + /// assert_eq!(*args.iter::().map(|num| num.unwrap().pow(2)).collect::>(), [9, 16]); + /// assert!(args.is_empty()); + /// ``` pub fn iter(&mut self) -> Iter 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::().unwrap(), 69); + /// assert_eq!(args, ["c47"]); + /// ``` pub fn find(&mut self) -> Result where T::Err: StdError { if self.delimiter_split.is_empty() { @@ -196,6 +292,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::().unwrap(), 69); + /// assert!(args == ["c47", "69"]); + /// ``` pub fn find_n(&self) -> Result where T::Err: StdError { if self.delimiter_split.is_empty() { @@ -216,6 +323,52 @@ impl ::std::ops::Deref for Args { 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; + } + } + + 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 { + >::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. -- cgit v1.2.3 From f5b19305f07fd5e5a63f9c919d1cf9160d49f21e Mon Sep 17 00:00:00 2001 From: acdenisSK Date: Wed, 18 Oct 2017 20:58:52 +0200 Subject: `assert` -> `assert_eq` --- src/framework/standard/args.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/framework') diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index 8190dd5..630143d 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -111,7 +111,7 @@ impl Args { /// let args = Args::new("42 69", " "); /// /// assert_eq!(args.single_n::().unwrap(), 42); - /// assert!(args == ["42", "69"]); + /// assert_eq!(args, ["42", "69"]); /// ``` /// /// [`single`]: #method.single @@ -301,7 +301,7 @@ impl Args { /// let args = Args::new("c47 69", " "); /// /// assert_eq!(args.find_n::().unwrap(), 69); - /// assert!(args == ["c47", "69"]); + /// assert_eq!(args, ["c47", "69"]); /// ``` pub fn find_n(&self) -> Result where T::Err: StdError { -- cgit v1.2.3 From bae0b4a3efde572543a4bf61e0a2484d8d451256 Mon Sep 17 00:00:00 2001 From: acdenisSK Date: Wed, 18 Oct 2017 21:02:00 +0200 Subject: Add a missing break --- src/framework/standard/args.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/framework') diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index 630143d..d299b64 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -330,6 +330,7 @@ impl<'a> PartialEq<[&'a str]> for Args { for (s, o) in self.delimiter_split.iter().zip(other.iter()) { if s != o { b = false; + break; } } -- cgit v1.2.3 From fbd625839e6a2e01b16e6c3814cb9b9f31dc7caa Mon Sep 17 00:00:00 2001 From: Mei Boudreau Date: Thu, 19 Oct 2017 22:50:38 -0400 Subject: Fix clippy warnings --- src/framework/standard/help_commands.rs | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) (limited to 'src/framework') diff --git a/src/framework/standard/help_commands.rs b/src/framework/standard/help_commands.rs index 0c018eb..5c1c58f 100644 --- a/src/framework/standard/help_commands.rs +++ b/src/framework/standard/help_commands.rs @@ -51,7 +51,7 @@ fn remove_aliases(cmds: &HashMap) -> HashMap<&String, &I result } -/// Checks whether a user is member of required roles +/// Checks whether a user is member of required roles /// and given the required permissions. pub fn has_all_requirements(cmd: &Command, msg: &Message) -> bool { if let Some(guild) = msg.guild() { @@ -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,19 +111,19 @@ 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 { 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 { @@ -230,12 +230,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; } } @@ -289,7 +286,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 { @@ -297,11 +294,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 { @@ -390,7 +387,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); } } -- cgit v1.2.3 From 9428787abb6126ba05bfef96cd2b8d2a217fdf5d Mon Sep 17 00:00:00 2001 From: acdenisSK Date: Sat, 21 Oct 2017 12:27:23 +0200 Subject: Add "zero-copy" parsing --- src/framework/standard/args.rs | 82 ++++++++++++++++++++++++++++++++++++++++++ src/framework/standard/mod.rs | 2 +- 2 files changed, 83 insertions(+), 1 deletion(-) (limited to 'src/framework') diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index d299b64..7fb82e4 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -101,6 +101,36 @@ impl Args { .parse::()?) } + /// 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 + 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) -> Option<&str> { + struct GetThenRemove<'a>(&'a mut Vec); + + 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 @@ -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, 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::().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; +} + +impl<'a, T: FromStr> FromStrZc<'a> for T { + type Err = T::Err; + + fn from_str(s: &'a str) -> ::std::result::Result { + ::from_str(s) + } +} + impl ::std::ops::Deref for Args { type Target = [String]; diff --git a/src/framework/standard/mod.rs b/src/framework/standard/mod.rs index 38c1d2d..947a885 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; -- cgit v1.2.3 From e02d5fb8171b11214e1502c6754fef1972bbf1b9 Mon Sep 17 00:00:00 2001 From: Lakelezz <12222135+Lakelezz@users.noreply.github.com> Date: Mon, 23 Oct 2017 21:10:47 +0200 Subject: Properly update emojis, fix shard retries, fix cs * If a guild's emojis are being altered, Serenity will straight up use the new `HashMap` instead of just extending. If `connect()` returns an `Err`, it will retry connecting. Cleaned up `help_command.rs`. --- src/framework/standard/help_commands.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/framework') diff --git a/src/framework/standard/help_commands.rs b/src/framework/standard/help_commands.rs index 5c1c58f..6b85239 100644 --- a/src/framework/standard/help_commands.rs +++ b/src/framework/standard/help_commands.rs @@ -113,8 +113,7 @@ pub fn with_embeds(_: &mut Context, CommandOrAlias::Command(ref cmd) => { if has_all_requirements(cmd, msg) { found = Some((command_name, cmd)); - } - else { + } else { break; } }, @@ -125,8 +124,7 @@ pub fn with_embeds(_: &mut Context, CommandOrAlias::Command(ref cmd) => { if has_all_requirements(cmd, msg) { found = Some((name, cmd)); - } - else { + } else { break; } }, -- cgit v1.2.3 From a58de97e6089aa98f04d2cdc7312ed38a9f72b22 Mon Sep 17 00:00:00 2001 From: acdenisSK Date: Mon, 23 Oct 2017 23:50:09 +0200 Subject: Add a debug impl for `DispatchError` Why this was hand-made instead of derived is because of `CheckFailed`'s content, which is mostly `Command` not also deriving `Debug`; except that even `Command` has a trouble maker that would force us to do this hand-made anyway, `checks`. Fixes #204 --- src/framework/standard/mod.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'src/framework') diff --git a/src/framework/standard/mod.rs b/src/framework/standard/mod.rs index 947a885..1a7a5ee 100644 --- a/src/framework/standard/mod.rs +++ b/src/framework/standard/mod.rs @@ -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. -- cgit v1.2.3