diff options
| author | acdenisSK <[email protected]> | 2017-11-14 15:02:10 +0100 |
|---|---|---|
| committer | acdenisSK <[email protected]> | 2017-11-15 21:09:16 +0100 |
| commit | f10b9d77f0b94864fa20688e3c99de6cec7ca6f9 (patch) | |
| tree | cb7bcd4244ce9813f07cf5eabaa95c71f8c9bb81 /src/framework | |
| parent | Use the threadpool for framework command execution (diff) | |
| download | serenity-f10b9d77f0b94864fa20688e3c99de6cec7ca6f9.tar.xz serenity-f10b9d77f0b94864fa20688e3c99de6cec7ca6f9.zip | |
Change most of the framework to use trait-based-commands
Diffstat (limited to 'src/framework')
| -rw-r--r-- | src/framework/standard/command.rs | 102 | ||||
| -rw-r--r-- | src/framework/standard/create_command.rs | 65 | ||||
| -rw-r--r-- | src/framework/standard/create_group.rs | 31 | ||||
| -rw-r--r-- | src/framework/standard/help_commands.rs | 23 | ||||
| -rw-r--r-- | src/framework/standard/mod.rs | 194 |
5 files changed, 243 insertions, 172 deletions
diff --git a/src/framework/standard/command.rs b/src/framework/standard/command.rs index 55a6dfd..8cf4fc5 100644 --- a/src/framework/standard/command.rs +++ b/src/framework/standard/command.rs @@ -5,11 +5,10 @@ use std::fmt; use std::sync::Arc; use super::{Args, Configuration}; -pub type Check = Fn(&mut Context, &Message, &mut Args, &Arc<Command>) -> bool +pub type Check = Fn(&mut Context, &Message, &mut Args, &CommandOptions) -> bool + Send + Sync + 'static; -pub type Exec = fn(&mut Context, &Message, Args) -> Result<(), Error>; pub type Help = fn(&mut Context, &Message, HashMap<String, Arc<CommandGroup>>, Args) -> Result<(), Error>; pub type BeforeHook = Fn(&mut Context, &Message, &str) -> bool + Send + Sync + 'static; @@ -17,12 +16,20 @@ pub type AfterHook = Fn(&mut Context, &Message, &str, Result<(), Error>) + Send pub(crate) type InternalCommand = Arc<Command>; pub type PrefixCheck = Fn(&mut Context, &Message) -> Option<String> + Send + Sync + 'static; -#[derive(Debug)] pub enum CommandOrAlias { Alias(String), Command(InternalCommand), } +impl fmt::Debug for CommandOrAlias { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + CommandOrAlias::Alias(ref s) => f.debug_tuple("CommandOrAlias::Alias").field(&s).finish(), + _ => Ok(()) + } + } +} + /// An error from a command. #[derive(Clone, Debug)] pub struct Error(pub String); @@ -30,18 +37,10 @@ pub struct Error(pub String); // TODO: Have seperate `From<(&)String>` and `From<&str>` impls via specialization impl<D: fmt::Display> From<D> for Error { fn from(d: D) -> Self { - Error(format!("{}", d)) + Error(d.to_string()) } } -/// Command function type. Allows to access internal framework things inside -/// your commands. -pub enum CommandType { - StringResponse(String), - Basic(Exec), - WithCommands(Help), -} - #[derive(Debug)] pub struct CommandGroup { pub prefix: Option<String>, @@ -56,13 +55,27 @@ pub struct CommandGroup { pub owners_only: bool, } -/// Command struct used to store commands internally. -pub struct Command { +impl Default for CommandGroup { + fn default() -> CommandGroup { + CommandGroup { + prefix: None, + commands: HashMap::new(), + bucket: None, + required_permissions: Permissions::empty(), + dm_only: false, + guild_only: false, + help_available: true, + owners_only: false, + allowed_roles: Vec::new(), + } + } +} + + +pub struct CommandOptions { /// A set of checks to be called prior to executing the command. The checks /// will short-circuit on the first check that returns `false`. pub checks: Vec<Box<Check>>, - /// Function called when the command is called. - pub exec: CommandType, /// Ratelimit bucket. pub bucket: Option<String>, /// Command description, used by other commands. @@ -91,19 +104,51 @@ pub struct Command { pub aliases: Vec<String>, } -impl Command { - pub fn new(f: fn(&mut Context, &Message, Args) -> Result<(), Error>) -> Self { - Command { - exec: CommandType::Basic(f), - ..Command::default() - } +lazy_static! { + static ref DEFAULT_OPTIONS: Arc<CommandOptions> = Arc::new(CommandOptions::default()); +} + +/// A framework command. +pub trait Command: Send + Sync + 'static { + fn execute(&self, &mut Context, &Message, Args) -> Result<(), Error>; + + fn options(&self) -> Arc<CommandOptions> { + Arc::clone(&DEFAULT_OPTIONS) + } +} + +impl Command for Arc<Command> { + fn execute(&self, c: &mut Context, m: &Message, a: Args) -> Result<(), Error> { + (**self).execute(c, m, a) + } + + fn options(&self) -> Arc<CommandOptions> { + (**self).options() + } +} + +impl Command for Box<Command> { + fn execute(&self, c: &mut Context, m: &Message, a: Args) -> Result<(), Error> { + (**self).execute(c, m, a) + } + + fn options(&self) -> Arc<CommandOptions> { + (**self).options() + } +} + +pub(crate) struct A(pub fn(&mut Context, &Message, Args) -> Result<(), Error>); + +impl Command for A { + fn execute(&self, c: &mut Context, m: &Message, a: Args) -> Result<(), Error> { + (self.0)(c, m, a) } } -impl fmt::Debug for Command { - // TODO: add Command::checks somehow? +impl fmt::Debug for CommandOptions { + // TODO: add CommandOptions::checks somehow? fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("Command") + fmt.debug_struct("CommandOptions") .field("bucket", &self.bucket) .field("desc", &self.desc) .field("example", &self.example) @@ -119,12 +164,11 @@ impl fmt::Debug for Command { } } -impl Default for Command { - fn default() -> Command { - Command { +impl Default for CommandOptions { + fn default() -> CommandOptions { + CommandOptions { aliases: Vec::new(), checks: Vec::default(), - exec: CommandType::Basic(|_, _, _| Ok(())), desc: None, usage: None, example: None, diff --git a/src/framework/standard/create_command.rs b/src/framework/standard/create_command.rs index cfddf06..4ba0106 100644 --- a/src/framework/standard/create_command.rs +++ b/src/framework/standard/create_command.rs @@ -1,12 +1,10 @@ -pub use super::{Args, Command, CommandGroup, CommandType, CommandError}; +pub use super::{Args, Command, CommandGroup, CommandOptions, CommandError}; use client::Context; use model::{Message, Permissions}; -use std::collections::HashMap; use std::sync::Arc; -#[derive(Debug)] -pub struct CreateCommand(pub Command); +pub struct CreateCommand(pub CommandOptions, pub fn(&mut Context, &Message, Args) -> Result<(), CommandError>); impl CreateCommand { /// Adds multiple aliases. @@ -40,13 +38,12 @@ impl CreateCommand { /// use serenity::client::{Client, Context}; /// use serenity::framework::standard::{ /// Args, - /// Command, + /// CommandOptions, /// CommandError, /// StandardFramework, /// }; /// use serenity::model::Message; /// use std::env; - /// use std::sync::Arc; /// /// let token = env::var("DISCORD_TOKEN").unwrap(); /// let mut client = Client::new(&token, Handler).unwrap(); @@ -66,13 +63,13 @@ impl CreateCommand { /// } /// /// fn owner_check(_context: &mut Context, message: &Message, _: &mut Args, _: - /// &Arc<Command>) -> bool { + /// &Arc<CommandOptions>) -> bool { /// // replace with your user ID /// message.author.id == 7 /// } /// ``` pub fn check<F>(mut self, check: F) -> Self - where F: Fn(&mut Context, &Message, &mut Args, &Arc<Command>) -> bool + where F: Fn(&mut Context, &Message, &mut Args, &CommandOptions) -> bool + Send + Sync + 'static { @@ -104,42 +101,8 @@ impl CreateCommand { /// A function that can be called when a command is received. /// You can return `Err(string)` if there's an error. - /// - /// See [`exec_str`] if you _only_ need to return a string on command use. - /// - /// [`exec_str`]: #method.exec_str pub fn exec(mut self, func: fn(&mut Context, &Message, Args) -> Result<(), CommandError>) -> Self { - self.0.exec = CommandType::Basic(func); - - self - } - - /// Sets a function that's called when a command is called that can access - /// the internal HashMap of commands, used specifically for creating a help - /// command. - /// - /// You can return `Err(From::from(string))` if there's an error. - pub fn exec_help(mut self, f: - fn(&mut Context, &Message, HashMap<String, Arc<CommandGroup>>, Args) - -> Result<(), CommandError>) -> Self { - self.0.exec = CommandType::WithCommands(f); - - self - } - - /// Sets a string to be sent in the channel of context on command. This can - /// be useful for an `about`, `invite`, `ping`, etc. command. - /// - /// # Examples - /// - /// Create a command named "ping" that returns "Pong!": - /// - /// ```rust,ignore - /// client.with_framework(|f| f - /// .command("ping", |c| c.exec_str("Pong!"))); - /// ``` - pub fn exec_str(mut self, content: &str) -> Self { - self.0.exec = CommandType::StringResponse(content.to_string()); + self.1 = func; self } @@ -215,5 +178,21 @@ impl CreateCommand { self } + + pub(crate) fn finish(self) -> Arc<Command> { + let CreateCommand(options, func) = self; + + struct A(Arc<CommandOptions>, fn(&mut Context, &Message, Args) -> Result<(), CommandError>); + + impl Command for A { + fn execute(&self, c: &mut Context, m: &Message, a: Args) -> Result<(), CommandError> { + (self.1)(c, m, a) + } + + fn options(&self) -> Arc<CommandOptions> { Arc::clone(&self.0) } + } + + Arc::new(A(Arc::new(options), func)) + } } diff --git a/src/framework/standard/create_group.rs b/src/framework/standard/create_group.rs index fb3ceb6..f9097e6 100644 --- a/src/framework/standard/create_group.rs +++ b/src/framework/standard/create_group.rs @@ -1,12 +1,11 @@ -pub use super::command::{Command, CommandGroup, CommandType, Error as CommandError}; +pub use super::command::{Command, CommandGroup, CommandOptions, Error as CommandError}; pub(crate) use super::command::CommandOrAlias; +pub(crate) use super::command::A; pub use super::create_command::CreateCommand; pub use super::Args; use client::Context; use model::{Message, Permissions}; -use std::collections::HashMap; -use std::default::Default; use std::sync::Arc; /// Used to create command groups @@ -28,7 +27,7 @@ pub struct CreateGroup(pub CommandGroup); impl CreateGroup { fn build_command(&self) -> CreateCommand { - let mut cmd = CreateCommand(Command::default()) + let mut cmd = CreateCommand(CommandOptions::default(), |_, _, _| Ok(())) .required_permissions(self.0.required_permissions) .dm_only(self.0.dm_only) .guild_only(self.0.guild_only) @@ -45,9 +44,9 @@ impl CreateGroup { /// Adds a command to group. pub fn command<F>(mut self, command_name: &str, f: F) -> Self where F: FnOnce(CreateCommand) -> CreateCommand { - let cmd = f(self.build_command()).0; + let cmd = f(self.build_command()).finish(); - for n in &cmd.aliases { + for n in &cmd.options().aliases { if let Some(ref prefix) = self.0.prefix { self.0.commands.insert( format!("{} {}", prefix, n.to_string()), @@ -63,7 +62,7 @@ impl CreateGroup { self.0.commands.insert( command_name.to_string(), - CommandOrAlias::Command(Arc::new(cmd)), + CommandOrAlias::Command(cmd), ); self @@ -73,7 +72,7 @@ impl CreateGroup { /// You can return Err(From::from(string)) if there's an error. pub fn on(mut self, name: &str, f: fn(&mut Context, &Message, Args) -> Result<(), CommandError>) -> Self { - let cmd = Arc::new(Command::new(f)); + let cmd = Arc::new(A(f)); self.0 .commands @@ -145,19 +144,3 @@ impl CreateGroup { self } } - -impl Default for CommandGroup { - fn default() -> CommandGroup { - CommandGroup { - prefix: None, - commands: HashMap::new(), - bucket: None, - required_permissions: Permissions::empty(), - dm_only: false, - guild_only: false, - help_available: true, - owners_only: false, - allowed_roles: Vec::new(), - } - } -} diff --git a/src/framework/standard/help_commands.rs b/src/framework/standard/help_commands.rs index 1e17c04..20fed5d 100644 --- a/src/framework/standard/help_commands.rs +++ b/src/framework/standard/help_commands.rs @@ -30,7 +30,7 @@ use std::collections::HashMap; use std::sync::Arc; use std::fmt::Write; use super::command::InternalCommand; -use super::{Args, Command, CommandGroup, CommandOrAlias, CommandError}; +use super::{Args, CommandGroup, CommandOrAlias, CommandOptions, CommandError}; use utils::Colour; fn error_embed(channel_id: &ChannelId, input: &str) { @@ -53,7 +53,7 @@ fn remove_aliases(cmds: &HashMap<String, CommandOrAlias>) -> HashMap<&String, &I /// Checks whether a user is member of required roles /// and given the required permissions. -pub fn has_all_requirements(cmd: &Command, msg: &Message) -> bool { +pub fn has_all_requirements(cmd: &Arc<CommandOptions>, msg: &Message) -> bool { if let Some(guild) = msg.guild() { let guild = guild.read(); @@ -111,7 +111,7 @@ 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.options(), msg) { found = Some((command_name, cmd)); } else { break; @@ -122,7 +122,7 @@ pub fn with_embeds(_: &mut Context, match *actual_command { CommandOrAlias::Command(ref cmd) => { - if has_all_requirements(cmd, msg) { + if has_all_requirements(&cmd.options(), msg) { found = Some((name, cmd)); } else { break; @@ -140,6 +140,7 @@ pub fn with_embeds(_: &mut Context, } if let Some((command_name, command)) = found { + let command = command.options(); if !command.help_available { error_embed(&msg.channel_id, "**Error**: No help available."); @@ -226,8 +227,9 @@ pub fn with_embeds(_: &mut Context, for name in command_names { let cmd = &commands[name]; + let cmd = cmd.options(); - if cmd.help_available && has_all_requirements(cmd, msg) { + if cmd.help_available && has_all_requirements(&cmd, msg) { let _ = write!(desc, "`{}`\n", name); has_commands = true; } @@ -271,7 +273,7 @@ pub fn plain(_: &mut Context, let name = args.full(); for (group_name, group) in groups { - let mut found: Option<(&String, &Command)> = None; + let mut found: Option<(&String, &InternalCommand)> = None; for (command_name, command) in &group.commands { let with_prefix = if let Some(ref prefix) = group.prefix { @@ -283,7 +285,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.options(), msg) { found = Some((command_name, cmd)); } else { @@ -295,7 +297,7 @@ pub fn plain(_: &mut Context, match *actual_command { CommandOrAlias::Command(ref cmd) => { - if has_all_requirements(cmd, msg) { + if has_all_requirements(&cmd.options(), msg) { found = Some((name, cmd)); } else { @@ -314,6 +316,8 @@ pub fn plain(_: &mut Context, } if let Some((command_name, command)) = found { + let command = command.options(); + if !command.help_available { let _ = msg.channel_id.say("**Error**: No help available."); return Ok(()); @@ -383,8 +387,9 @@ pub fn plain(_: &mut Context, for name in command_names { let cmd = &commands[name]; + let cmd = cmd.options(); - 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 dd7aaae..106d64c 100644 --- a/src/framework/standard/mod.rs +++ b/src/framework/standard/mod.rs @@ -10,7 +10,8 @@ mod args; pub use self::args::{Args, Iter, FromStrZc, Error as ArgError}; pub(crate) use self::buckets::{Bucket, Ratelimit}; -pub use self::command::{Command, CommandGroup, CommandType, Error as CommandError}; +pub(crate) use self::command::{A, Help}; +pub use self::command::{Command, CommandGroup, CommandOptions, Error as CommandError}; pub use self::command::CommandOrAlias; pub use self::configuration::Configuration; pub use self::create_command::CreateCommand; @@ -68,45 +69,65 @@ use model::Channel; #[macro_export] macro_rules! command { ($fname:ident($c:ident) $b:block) => { - #[allow(unreachable_code, unused_mut)] - pub fn $fname(mut $c: &mut $crate::client::Context, + #[allow(non_camel_case_types)] + struct $fname; + + impl $crate::framework::standard::Command for $fname { + #[allow(unreachable_code, unused_mut)] + fn execute(&self, mut $c: &mut $crate::client::Context, _: &$crate::model::Message, _: $crate::framework::standard::Args) -> ::std::result::Result<(), $crate::framework::standard::CommandError> { - $b + + $b - Ok(()) + Ok(()) + } } }; ($fname:ident($c:ident, $m:ident) $b:block) => { - #[allow(unreachable_code, unused_mut)] - pub fn $fname(mut $c: &mut $crate::client::Context, + #[allow(non_camel_case_types)] + struct $fname; + + impl $crate::framework::standard::Command for $fname { + #[allow(unreachable_code, unused_mut)] + fn execute(&self, mut $c: &mut $crate::client::Context, $m: &$crate::model::Message, _: $crate::framework::standard::Args) -> ::std::result::Result<(), $crate::framework::standard::CommandError> { - $b + + $b - Ok(()) + Ok(()) + } } }; ($fname:ident($c:ident, $m:ident, $a:ident) $b:block) => { - #[allow(unreachable_code, unused_mut)] - pub fn $fname(mut $c: &mut $crate::client::Context, + #[allow(non_camel_case_types)] + struct $fname; + + impl $crate::framework::standard::Command for $fname { + #[allow(unreachable_code, unused_mut)] + fn execute(&self, mut $c: &mut $crate::client::Context, $m: &$crate::model::Message, mut $a: $crate::framework::standard::Args) -> ::std::result::Result<(), $crate::framework::standard::CommandError> { - $b + + $b - Ok(()) + Ok(()) + } } }; } -/// A enum representing all possible fail conditions under which a command won't +/// An enum representing all possible fail conditions under which a command won't /// be executed. pub enum DispatchError { /// When a custom function check has failed. - CheckFailed(Arc<Command>), + // + // TODO: Bring back `Arc<Command>` as `CommandOptions` here somehow? + CheckFailed, /// When the requested command is disabled in bot configuration. CommandDisabled(String), /// When the user is blocked in bot configuration. @@ -146,7 +167,7 @@ impl fmt::Debug for DispatchError { use self::DispatchError::*; match *self { - CheckFailed(..) => write!(f, "DispatchError::CheckFailed"), + 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"), @@ -175,6 +196,7 @@ type DispatchErrorHook = Fn(Context, Message, DispatchError) + Send + Sync + 'st pub struct StandardFramework { configuration: Configuration, groups: HashMap<String, Arc<CommandGroup>>, + help: Option<Arc<Help>>, before: Option<Arc<BeforeHook>>, dispatch_error_handler: Option<Arc<DispatchErrorHook>>, buckets: HashMap<String, Bucket>, @@ -263,10 +285,9 @@ impl StandardFramework { /// .bucket("basic") /// .exec_str("pong!"))); /// ``` - pub fn bucket<S>(mut self, s: S, delay: i64, time_span: i64, limit: i32) -> Self - where S: Into<String> { + pub fn bucket(mut self, s: &str, delay: i64, time_span: i64, limit: i32) -> Self { self.buckets.insert( - s.into(), + s.to_string(), Bucket { ratelimit: Ratelimit { delay: delay, @@ -309,8 +330,8 @@ impl StandardFramework { /// /// [`bucket`]: #method.bucket #[cfg(feature = "cache")] - pub fn complex_bucket<S, Check>(mut self, - s: S, + pub fn complex_bucket<Check>(mut self, + s: &str, delay: i64, time_span: i64, limit: i32, @@ -319,10 +340,9 @@ impl StandardFramework { where Check: Fn(&mut Context, Option<GuildId>, ChannelId, UserId) -> bool + Send + Sync - + 'static, - S: Into<String> { + + 'static { self.buckets.insert( - s.into(), + s.to_string(), Bucket { ratelimit: Ratelimit { delay, @@ -363,17 +383,16 @@ impl StandardFramework { /// /// [`bucket`]: #method.bucket #[cfg(not(feature = "cache"))] - pub fn complex_bucket<S, Check>(mut self, - s: S, + pub fn complex_bucket<Check>(mut self, + s: &str, delay: i64, time_span: i64, limit: i32, check: Check) -> Self - where Check: Fn(&mut Context, ChannelId, UserId) -> bool + Send + Sync + 'static, - S: Into<String> { + where Check: Fn(&mut Context, ChannelId, UserId) -> bool + Send + Sync + 'static { self.buckets.insert( - s.into(), + s.to_string(), Bucket { ratelimit: Ratelimit { delay, @@ -408,10 +427,9 @@ impl StandardFramework { /// .bucket("simple") /// .exec_str("pong!"))); /// ``` - pub fn simple_bucket<S>(mut self, s: S, delay: i64) -> Self - where S: Into<String> { + pub fn simple_bucket<S>(mut self, s: &str, delay: i64) -> Self { self.buckets.insert( - s.into(), + s.to_string(), Bucket { ratelimit: Ratelimit { delay: delay, @@ -448,7 +466,7 @@ impl StandardFramework { fn should_fail(&mut self, mut context: &mut Context, message: &Message, - command: &Arc<Command>, + command: &Arc<CommandOptions>, args: &mut Args, to_check: &str, built: &str) @@ -559,7 +577,7 @@ impl StandardFramework { if all_passed { None } else { - Some(DispatchError::CheckFailed(Arc::clone(command))) + Some(DispatchError::CheckFailed) } } } @@ -616,7 +634,29 @@ impl StandardFramework { if let Some(ref mut group) = Arc::get_mut(ungrouped) { group .commands - .insert(name.to_string(), CommandOrAlias::Command(Arc::new(Command::new(f)))); + .insert(name.to_string(), CommandOrAlias::Command(Arc::new(A(f)))); + } + } + + self.initialized = true; + + self + } + + /// Same as [`on`], but accepts a [`Command`] directly. + /// + /// [`on`]: #method.on + /// [`Command`]: trait.Command.html + pub fn cmd<C: Command + 'static>(mut self, name: &str, c: C) -> Self { + { + let ungrouped = self.groups + .entry("Ungrouped".to_string()) + .or_insert_with(|| Arc::new(CommandGroup::default())); + + if let Some(ref mut group) = Arc::get_mut(ungrouped) { + group + .commands + .insert(name.to_string(), CommandOrAlias::Command(Arc::new(c))); } } @@ -636,26 +676,26 @@ impl StandardFramework { /// let _ = ctx.say("pong"); /// })); /// ``` - pub fn command<F, S>(mut self, command_name: S, f: F) -> Self - where F: FnOnce(CreateCommand) -> CreateCommand, S: Into<String> { + pub fn command<F, S>(mut self, command_name: &str, f: F) -> Self + where F: FnOnce(CreateCommand) -> CreateCommand { { let ungrouped = self.groups .entry("Ungrouped".to_string()) .or_insert_with(|| Arc::new(CommandGroup::default())); if let Some(ref mut group) = Arc::get_mut(ungrouped) { - let cmd = f(CreateCommand(Command::default())).0; - let name = command_name.into(); + let cmd = f(CreateCommand(CommandOptions::default(), |_, _, _| Ok(()))).finish(); + let name = command_name.to_string(); if let Some(ref prefix) = group.prefix { - for v in &cmd.aliases { + for v in &cmd.options().aliases { group.commands.insert( format!("{} {}", prefix, v), CommandOrAlias::Alias(format!("{} {}", prefix, name)), ); } } else { - for v in &cmd.aliases { + for v in &cmd.options().aliases { group .commands .insert(v.to_string(), CommandOrAlias::Alias(name.clone())); @@ -664,7 +704,7 @@ impl StandardFramework { group .commands - .insert(name, CommandOrAlias::Command(Arc::new(cmd))); + .insert(name, CommandOrAlias::Command(cmd)); } } @@ -673,6 +713,13 @@ impl StandardFramework { self } + /// Sets what code should be execute when a user requests for `(prefix)help`. + pub fn help(mut self, f: Help) -> Self { + self.help = Some(Arc::new(f)); + + self + } + /// Adds a group which can organize several related commands. /// Groups are taken into account when using /// `serenity::framework::standard::help_commands`. @@ -904,24 +951,47 @@ impl Framework for StandardFramework { to_check }; - if let Some(&CommandOrAlias::Command(ref command)) = - group.commands.get(&to_check) { - let before = self.before.clone(); - let command = Arc::clone(command); - let after = self.after.clone(); - let groups = self.groups.clone(); + let mut args = { + let mut content = message.content[position..].trim(); + content = content[command_length..].trim(); - let mut args = { - let mut content = message.content[position..].trim(); - content = content[command_length..].trim(); + Args::new(&content, self.configuration.delimiters.clone()) + }; - Args::new(&content, self.configuration.delimiters.clone()) - }; + let before = self.before.clone(); + let after = self.after.clone(); + + // This is a special case. + if to_check == "help" { + let help = self.help.clone(); + if let Some(help) = help { + let groups = self.groups.clone(); + threadpool.execute(move || { + if let Some(before) = before { + if !(before)(&mut context, &message, &built) { + return; + } + } + let result = (help)(&mut context, &message, groups, args); + + if let Some(after) = after { + (after)(&mut context, &message, &built, result); + } + }); + return; + } + + return; + } + + if let Some(&CommandOrAlias::Command(ref command)) = + group.commands.get(&to_check) { + let command = Arc::clone(command); if let Some(error) = self.should_fail( &mut context, &message, - &command, + &command.options(), &mut args, &to_check, &built, @@ -939,17 +1009,7 @@ impl Framework for StandardFramework { } } - let result = match command.exec { - CommandType::StringResponse(ref x) => { - let _ = message.channel_id.say(x); - - Ok(()) - }, - CommandType::Basic(ref x) => (x)(&mut context, &message, args), - CommandType::WithCommands(ref x) => { - (x)(&mut context, &message, groups, args) - }, - }; + let result = command.execute(&mut context, &message, args); if let Some(after) = after { (after)(&mut context, &message, &built, result); @@ -969,7 +1029,7 @@ impl Framework for StandardFramework { } #[cfg(feature = "cache")] -pub fn has_correct_permissions(command: &Command, message: &Message) -> bool { +pub fn has_correct_permissions(command: &Arc<CommandOptions>, message: &Message) -> bool { if !command.required_permissions.is_empty() { if let Some(guild) = message.guild() { let perms = guild @@ -983,7 +1043,7 @@ pub fn has_correct_permissions(command: &Command, message: &Message) -> bool { } #[cfg(feature = "cache")] -pub fn has_correct_roles(cmd: &Command, guild: &Guild, member: &Member) -> bool { +pub fn has_correct_roles(cmd: &Arc<CommandOptions>, guild: &Guild, member: &Member) -> bool { cmd.allowed_roles .iter() .flat_map(|r| guild.role_by_name(r)) |