diff options
| author | taavi? <[email protected]> | 2016-12-29 23:21:57 +0300 |
|---|---|---|
| committer | zeyla <[email protected]> | 2016-12-29 12:21:57 -0800 |
| commit | f96b6cc5e1e0383fd2de826c8ffd95565d5ca4fb (patch) | |
| tree | e49ca34f9a6ce8bda865bed82c8bd2519c4cbcc4 /src/ext/framework | |
| parent | Remove use of struct pattern match (diff) | |
| download | serenity-f96b6cc5e1e0383fd2de826c8ffd95565d5ca4fb.tar.xz serenity-f96b6cc5e1e0383fd2de826c8ffd95565d5ca4fb.zip | |
Add command alias support and command.example
Diffstat (limited to 'src/ext/framework')
| -rw-r--r-- | src/ext/framework/command.rs | 16 | ||||
| -rw-r--r-- | src/ext/framework/create_command.rs | 27 | ||||
| -rw-r--r-- | src/ext/framework/create_group.rs | 16 | ||||
| -rw-r--r-- | src/ext/framework/help_commands.rs | 120 | ||||
| -rw-r--r-- | src/ext/framework/mod.rs | 40 |
5 files changed, 155 insertions, 64 deletions
diff --git a/src/ext/framework/command.rs b/src/ext/framework/command.rs index acddca9..92e6e93 100644 --- a/src/ext/framework/command.rs +++ b/src/ext/framework/command.rs @@ -14,6 +14,12 @@ pub type AfterHook = Fn(&Context, &Message, &String, Result<(), String>) + Send pub type InternalCommand = Arc<Command>; pub type PrefixCheck = Fn(&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 { @@ -25,7 +31,7 @@ pub enum CommandType { #[derive(Default)] pub struct CommandGroup { pub prefix: Option<String>, - pub commands: HashMap<String, InternalCommand>, + pub commands: HashMap<String, CommandOrAlias>, } /// Command struct used to store commands internally. @@ -39,6 +45,8 @@ pub struct Command { 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. @@ -57,16 +65,20 @@ pub struct Command { 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(&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, @@ -74,8 +86,8 @@ impl Command { help_available: true, min_args: None, max_args: None, - required_permissions: Permissions::empty(), owners_only: false, + required_permissions: Permissions::empty(), } } } diff --git a/src/ext/framework/create_command.rs b/src/ext/framework/create_command.rs index 06d412a..c758f4a 100644 --- a/src/ext/framework/create_command.rs +++ b/src/ext/framework/create_command.rs @@ -16,6 +16,22 @@ impl CreateCommand { self } + /// Adds an alias, allowing users to use the command under a different name. + pub fn known_as(mut self, name: &str) -> Self { + self.0.aliases.push(name.to_owned()); + + self + } + + /// Adds multiple aliases. + pub fn batch_known_as(mut self, names: Vec<&str>) -> Self { + for n in names { + self.0.aliases.push(n.to_owned()); + } + + self + } + /// Adds a "check" to a command, which checks whether or not the command's /// function should be called. /// @@ -70,6 +86,13 @@ impl CreateCommand { self } + /// Example arguments, used by other commands. + pub fn example(mut self, example: &str) -> Self { + self.0.example = Some(example.to_owned()); + + self + } + /// A function that can be called when a command is received. /// You can return Err(string) if there's an error. /// @@ -183,10 +206,12 @@ impl CreateCommand { impl Default for Command { fn default() -> Command { Command { + aliases: Vec::new(), checks: Vec::default(), exec: CommandType::Basic(Box::new(|_, _, _| Ok(()))), desc: None, usage: None, + example: None, use_quotes: false, min_args: None, bucket: None, @@ -195,7 +220,7 @@ impl Default for Command { dm_only: false, guild_only: false, help_available: true, - owners_only: false + owners_only: false, } } } diff --git a/src/ext/framework/create_group.rs b/src/ext/framework/create_group.rs index 8374e8b..465224d 100644 --- a/src/ext/framework/create_group.rs +++ b/src/ext/framework/create_group.rs @@ -1,4 +1,4 @@ -pub use ext::framework::command::{Command, CommandType, CommandGroup}; +pub use ext::framework::command::{Command, CommandType, CommandGroup, CommandOrAlias}; pub use ext::framework::create_command::CreateCommand; use std::default::Default; @@ -29,7 +29,15 @@ impl CreateGroup { where F: FnOnce(CreateCommand) -> CreateCommand { let cmd = f(CreateCommand(Command::default())).0; - self.0.commands.insert(command_name.to_owned(), Arc::new(cmd)); + for n in &cmd.aliases { + if let Some(ref prefix) = self.0.prefix { + self.0.commands.insert(format!("{} {}", prefix, n.to_owned()), CommandOrAlias::Alias(format!("{} {}", prefix, command_name.to_string()))); + } else { + self.0.commands.insert(n.to_owned(), CommandOrAlias::Alias(command_name.to_string())); + } + } + + self.0.commands.insert(command_name.to_owned(), CommandOrAlias::Command(Arc::new(cmd))); self } @@ -40,7 +48,7 @@ impl CreateGroup { where F: Fn(&Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static { let cmd = Arc::new(Command::new(f)); - self.0.commands.insert(command_name.to_owned(), cmd); + self.0.commands.insert(command_name.to_owned(), CommandOrAlias::Command(cmd)); self } @@ -50,6 +58,8 @@ impl CreateGroup { /// we'd call a subcommand named "hibiki" by sending "~image hibiki". /// /// **Note**: serenity automatically puts a space after group prefix. + /// + /// **Note**: It's suggested to call this first when making a group. pub fn prefix(mut self, desc: &str) -> Self { self.0.prefix = Some(desc.to_owned()); diff --git a/src/ext/framework/help_commands.rs b/src/ext/framework/help_commands.rs index e071be5..230d78a 100644 --- a/src/ext/framework/help_commands.rs +++ b/src/ext/framework/help_commands.rs @@ -1,7 +1,8 @@ use std::collections::HashMap; use std::sync::Arc; use std::fmt::Write; -use super::{Command, CommandGroup}; +use super::command::InternalCommand; +use super::{Command, CommandGroup, CommandOrAlias}; use ::client::Context; use ::model::Message; use ::utils::Colour; @@ -13,6 +14,18 @@ fn error_embed(ctx: &Context, message: &Message, input: &str) { .description(input))); } +fn remove_aliases(cmds: &HashMap<String, CommandOrAlias>) -> HashMap<&String, &InternalCommand> { + let mut result = HashMap::new(); + + for (n, v) in cmds { + if let CommandOrAlias::Command(ref cmd) = *v { + result.insert(n, cmd); + } + } + + result +} + pub fn with_embeds(ctx: &Context, message: &Message, groups: HashMap<String, Arc<CommandGroup>>, @@ -21,21 +34,27 @@ pub fn with_embeds(ctx: &Context, let name = args.join(" "); for (group_name, group) in groups { - let mut found: Option<(&String, &Command)> = None; + let mut found: Option<(&String, &InternalCommand)> = None; - if let Some(ref prefix) = group.prefix { - for (command_name, command) in &group.commands { - if name == format!("{} {}", prefix, command_name) { - found = Some((command_name, command)); - } - } - } else { - for (command_name, command) in &group.commands { - if name == command_name[..] { - found = Some((command_name, command)); + for (command_name, command) in &group.commands { + let with_prefix = if let Some(ref prefix) = group.prefix { + format!("{} {}", prefix, command_name) + } else { + command_name.to_owned() + }; + + if name == with_prefix || name == *command_name { + match *command { + CommandOrAlias::Command(ref cmd) => { + found = Some((command_name, cmd)); + }, + CommandOrAlias::Alias(ref name) => { + error_embed(ctx, message, &format!("Did you mean \"{}\"?", name)); + return Ok(()); + } } } - }; + } if let Some((command_name, command)) = found { if !command.help_available { @@ -49,25 +68,25 @@ pub fn with_embeds(ctx: &Context, let mut embed = e.colour(Colour::rosewater()) .title(command_name); if let Some(ref desc) = command.desc { - embed = embed.field(|f| { - f.name("Description") - .value(desc) - .inline(false) - }); + embed = embed.description(desc); } if let Some(ref usage) = command.usage { - embed = embed.field(|f| { - f.name("Usage") - .value(&format!("{} {}", command_name, usage)) - }); + embed = embed.field(|f| f + .name("Usage") + .value(&format!("`{} {}`", command_name, usage))); + } + + if let Some(ref example) = command.example { + embed = embed.field(|f| f + .name("Sample usage") + .value(&format!("`{} {}`", command_name, example))); } if group_name != "Ungrouped" { - embed = embed.field(|f| { - f.name("Group") - .value(&group_name) - }); + embed = embed.field(|f| f + .name("Group") + .value(&group_name)); } let available = if command.dm_only { @@ -78,10 +97,9 @@ pub fn with_embeds(ctx: &Context, "In DM and guilds" }; - embed = embed.field(|f| { - f.name("Available") - .value(available) - }); + embed = embed.field(|f| f + .name("Available") + .value(available)); embed }) @@ -114,7 +132,7 @@ pub fn with_embeds(ctx: &Context, let mut no_commands = true; - for (n, cmd) in &group.commands { + for (n, cmd) in remove_aliases(&group.commands) { if cmd.help_available { let _ = write!(desc, "`{}`\n", n); @@ -146,19 +164,25 @@ pub fn plain(ctx: &Context, for (group_name, group) in groups { let mut found: Option<(&String, &Command)> = None; - if let Some(ref prefix) = group.prefix { - for (command_name, command) in &group.commands { - if name == format!("{} {}", prefix, command_name) { - found = Some((command_name, command)); - } - } - } else { - for (command_name, command) in &group.commands { - if name == command_name[..] { - found = Some((command_name, command)); + for (command_name, command) in &group.commands { + let with_prefix = if let Some(ref prefix) = group.prefix { + format!("{} {}", prefix, command_name) + } else { + command_name.to_owned() + }; + + if name == with_prefix || name == *command_name { + match *command { + CommandOrAlias::Command(ref cmd) => { + found = Some((command_name, cmd)); + }, + CommandOrAlias::Alias(ref name) => { + let _ = ctx.say(&format!("Did you mean {:?}?", name)); + return Ok(()); + } } } - }; + } if let Some((command_name, command)) = found { if !command.help_available { @@ -173,7 +197,11 @@ pub fn plain(ctx: &Context, } if let Some(ref usage) = command.usage { - let _ = write!(result, "**Usage:** {}\n", usage); + let _ = write!(result, "**Usage:** `{} {}`\n", command_name, usage); + } + + if let Some(ref example) = command.example { + let _ = write!(result, "**Sample usage:** `{} {}`\n", command_name, example); } if group_name != "Ungrouped" { @@ -201,8 +229,8 @@ pub fn plain(ctx: &Context, return Ok(()); } - let mut result = "**Commands**\nTo get help about individual command, pass \ - its name as an argument to this command.\n\n" + let mut result = "**Commands**\nTo get help with an individual command, pass its \ + name as an argument to this command.\n\n" .to_string(); for (group_name, group) in groups { @@ -214,7 +242,7 @@ pub fn plain(ctx: &Context, let mut no_commands = true; - for (n, cmd) in &group.commands { + for (n, cmd) in remove_aliases(&group.commands) { if cmd.help_available { let _ = write!(result, "`{}` ", n); diff --git a/src/ext/framework/mod.rs b/src/ext/framework/mod.rs index 28994b8..4516571 100644 --- a/src/ext/framework/mod.rs +++ b/src/ext/framework/mod.rs @@ -62,7 +62,7 @@ mod create_group; mod buckets; pub use self::buckets::{Bucket, MemberRatelimit, Ratelimit}; -pub use self::command::{Command, CommandType, CommandGroup}; +pub use self::command::{Command, CommandType, CommandGroup, CommandOrAlias}; pub use self::configuration::{AccountType, Configuration}; pub use self::create_command::CreateCommand; pub use self::create_group::CreateGroup; @@ -113,28 +113,28 @@ use ::client::CACHE; #[macro_export] macro_rules! command { ($fname:ident($c:ident) $b:block) => { - pub fn $fname($c: &$crate::client::Context, _: &$crate::model::Message, _: Vec<String>) -> Result<(), String> { + pub fn $fname($c: &$crate::client::Context, _: &$crate::model::Message, _: Vec<String>) -> std::result::Result<(), String> { $b Ok(()) } }; ($fname:ident($c:ident, $m:ident) $b:block) => { - pub fn $fname($c: &$crate::client::Context, $m: &$crate::model::Message, _: Vec<String>) -> Result<(), String> { + pub fn $fname($c: &$crate::client::Context, $m: &$crate::model::Message, _: Vec<String>) -> std::result::Result<(), String> { $b Ok(()) } }; ($fname:ident($c:ident, $m:ident, $a:ident) $b:block) => { - pub fn $fname($c: &$crate::client::Context, $m: &$crate::model::Message, $a: Vec<String>) -> Result<(), String> { + pub fn $fname($c: &$crate::client::Context, $m: &$crate::model::Message, $a: Vec<String>) -> std::result::Result<(), String> { $b Ok(()) } }; ($fname:ident($c:ident, $m:ident, $a:ident, $($name:ident: $t:ty),*) $b:block) => { - pub fn $fname($c: &$crate::client::Context, $m: &$crate::model::Message, $a: Vec<String>) -> Result<(), String> { + pub fn $fname($c: &$crate::client::Context, $m: &$crate::model::Message, $a: Vec<String>) -> std::result::Result<(), String> { let mut i = $a.iter(); let mut arg_counter = 0; @@ -322,8 +322,14 @@ impl Framework { let groups = self.groups.clone(); for group in groups.values() { + let command_length = built.len(); + + if let Some(&CommandOrAlias::Alias(ref points_to)) = group.commands.get(&built) { + built = points_to.to_owned(); + } + let to_check = if let Some(ref prefix) = group.prefix { - if built.starts_with(prefix) && built.len() > prefix.len() + 1 { + if built.starts_with(prefix) && command_length > prefix.len() + 1 { built[(prefix.len() + 1)..].to_owned() } else { continue; @@ -332,7 +338,7 @@ impl Framework { built.clone() }; - if let Some(command) = group.commands.get(&to_check) { + if let Some(&CommandOrAlias::Command(ref command)) = group.commands.get(&to_check) { let is_owner = self.configuration.owners.contains(&message.author.id); // Most of the checks don't apply to owners. if !is_owner { @@ -447,9 +453,9 @@ impl Framework { let groups = self.groups.clone(); let args = if command.use_quotes { - utils::parse_quotes(&message.content[position + built.len()..]) + utils::parse_quotes(&message.content[position + command_length..]) } else { - message.content[position + built.len()..] + message.content[position + command_length..] .split_whitespace() .map(|arg| arg.to_owned()) .collect::<Vec<String>>() @@ -566,7 +572,7 @@ impl Framework { if let Some(ref mut group) = Arc::get_mut(ungrouped) { let name = command_name.into(); - group.commands.insert(name, Arc::new(Command::new(f))); + group.commands.insert(name, CommandOrAlias::Command(Arc::new(Command::new(f)))); } } @@ -597,7 +603,17 @@ impl Framework { let cmd = f(CreateCommand(Command::default())).0; let name = command_name.into(); - group.commands.insert(name, Arc::new(cmd)); + if let Some(ref prefix) = group.prefix { + for v in &cmd.aliases { + group.commands.insert(format!("{} {}", prefix, v.to_owned()), CommandOrAlias::Alias(format!("{} {}", prefix, name))); + } + } else { + for v in &cmd.aliases { + group.commands.insert(v.to_owned(), CommandOrAlias::Alias(name.clone())); + } + } + + group.commands.insert(name, CommandOrAlias::Command(Arc::new(cmd))); } } @@ -675,7 +691,7 @@ impl Framework { if let Some(group) = Arc::get_mut(ungrouped) { let name = command.into(); - if let Some(ref mut command) = group.commands.get_mut(&name) { + if let Some(&mut CommandOrAlias::Command(ref mut command)) = group.commands.get_mut(&name) { if let Some(command) = Arc::get_mut(command) { command.checks.push(Box::new(check)); } |