aboutsummaryrefslogtreecommitdiff
path: root/src/framework
diff options
context:
space:
mode:
authoracdenisSK <[email protected]>2017-11-14 15:02:10 +0100
committeracdenisSK <[email protected]>2017-11-15 21:09:16 +0100
commitf10b9d77f0b94864fa20688e3c99de6cec7ca6f9 (patch)
treecb7bcd4244ce9813f07cf5eabaa95c71f8c9bb81 /src/framework
parentUse the threadpool for framework command execution (diff)
downloadserenity-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.rs102
-rw-r--r--src/framework/standard/create_command.rs65
-rw-r--r--src/framework/standard/create_group.rs31
-rw-r--r--src/framework/standard/help_commands.rs23
-rw-r--r--src/framework/standard/mod.rs194
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))