aboutsummaryrefslogtreecommitdiff
path: root/src/framework/command.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/framework/command.rs')
-rw-r--r--src/framework/command.rs164
1 files changed, 164 insertions, 0 deletions
diff --git a/src/framework/command.rs b/src/framework/command.rs
new file mode 100644
index 0000000..5f43284
--- /dev/null
+++ b/src/framework/command.rs
@@ -0,0 +1,164 @@
+use std::sync::Arc;
+use super::Configuration;
+use ::client::Context;
+use ::model::Message;
+use ::model::Permissions;
+use std::collections::HashMap;
+
+pub type Check = Fn(&mut Context, &Message) -> bool + Send + Sync + 'static;
+pub type Exec = Fn(&mut Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static;
+pub type Help = Fn(&mut Context, &Message, HashMap<String, Arc<CommandGroup>>, &[String]) -> Result<(), String> + Send + Sync + 'static;
+pub type BeforeHook = Fn(&mut Context, &Message, &String) -> bool + Send + Sync + 'static;
+pub type AfterHook = Fn(&mut Context, &Message, &String, Result<(), String>) + Send + Sync + 'static;
+#[doc(hidden)]
+pub type InternalCommand = Arc<Command>;
+pub type PrefixCheck = Fn(&mut Context) -> Option<String> + Send + Sync + 'static;
+
+#[doc(hidden)]
+pub enum CommandOrAlias {
+ Alias(String),
+ Command(InternalCommand),
+}
+
+/// Command function type. Allows to access internal framework things inside
+/// your commands.
+pub enum CommandType {
+ StringResponse(String),
+ Basic(Box<Exec>),
+ WithCommands(Box<Help>),
+}
+
+#[derive(Default)]
+pub struct CommandGroup {
+ pub prefix: Option<String>,
+ pub commands: HashMap<String, CommandOrAlias>,
+}
+
+/// Command struct used to store commands internally.
+pub struct Command {
+ /// 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.
+ pub desc: Option<String>,
+ /// Example arguments, used by other commands.
+ pub example: Option<String>,
+ /// Command usage schema, used by other commands.
+ pub usage: Option<String>,
+ /// Whether arguments should be parsed using quote parser or not.
+ pub use_quotes: bool,
+ /// Minumum amount of arguments that should be passed.
+ pub min_args: Option<i32>,
+ /// Maximum amount of arguments that can be passed.
+ pub max_args: Option<i32>,
+ /// Permissions required to use this command.
+ pub required_permissions: Permissions,
+ /// Whether command should be displayed in help list or not, used by other commands.
+ pub help_available: bool,
+ /// Whether command can be used only privately or not.
+ pub dm_only: bool,
+ /// Whether command can be used only in guilds or not.
+ pub guild_only: bool,
+ /// Whether command can only be used by owners or not.
+ pub owners_only: bool,
+ #[doc(hidden)]
+ pub aliases: Vec<String>,
+}
+
+impl Command {
+ pub fn new<F>(f: F) -> Self
+ where F: Fn(&mut Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static {
+ Command {
+ aliases: Vec::new(),
+ checks: Vec::default(),
+ exec: CommandType::Basic(Box::new(f)),
+ desc: None,
+ usage: None,
+ example: None,
+ use_quotes: false,
+ dm_only: false,
+ bucket: None,
+ guild_only: false,
+ help_available: true,
+ min_args: None,
+ max_args: None,
+ owners_only: false,
+ required_permissions: Permissions::empty(),
+ }
+ }
+}
+
+pub fn positions(ctx: &mut Context, content: &str, conf: &Configuration) -> Option<Vec<usize>> {
+ if !conf.prefixes.is_empty() || conf.dynamic_prefix.is_some() {
+ // Find out if they were mentioned. If not, determine if the prefix
+ // was used. If not, return None.
+ let mut positions: Vec<usize> = vec![];
+
+ if let Some(mention_end) = find_mention_end(content, conf) {
+ positions.push(mention_end);
+ } else if let Some(ref func) = conf.dynamic_prefix {
+ if let Some(x) = func(ctx) {
+ if content.starts_with(&x) {
+ positions.push(x.len());
+ }
+ } else {
+ for n in conf.prefixes.clone() {
+ if content.starts_with(&n) {
+ positions.push(n.len());
+ }
+ }
+ }
+ } else {
+ for n in conf.prefixes.clone() {
+ if content.starts_with(&n) {
+ positions.push(n.len());
+ }
+ }
+ };
+
+ if positions.is_empty() {
+ return None;
+ }
+
+ if conf.allow_whitespace {
+ let pos = *unsafe { positions.get_unchecked(0) };
+
+ positions.insert(0, pos + 1);
+ }
+
+ Some(positions)
+ } else if conf.on_mention.is_some() {
+ match find_mention_end(content, conf) {
+ Some(mention_end) => {
+ let mut positions = vec![mention_end];
+
+ if conf.allow_whitespace {
+ positions.insert(0, mention_end + 1);
+ }
+
+ Some(positions)
+ },
+ None => None,
+ }
+ } else {
+ None
+ }
+}
+
+fn find_mention_end(content: &str, conf: &Configuration) -> Option<usize> {
+ if let Some(ref mentions) = conf.on_mention {
+ for mention in mentions {
+ if !content.starts_with(&mention[..]) {
+ continue;
+ }
+
+ return Some(mention.len());
+ }
+ }
+
+ None
+}