aboutsummaryrefslogtreecommitdiff
path: root/src/ext/framework
diff options
context:
space:
mode:
authortaavi? <[email protected]>2016-12-29 23:21:57 +0300
committerzeyla <[email protected]>2016-12-29 12:21:57 -0800
commitf96b6cc5e1e0383fd2de826c8ffd95565d5ca4fb (patch)
treee49ca34f9a6ce8bda865bed82c8bd2519c4cbcc4 /src/ext/framework
parentRemove use of struct pattern match (diff)
downloadserenity-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.rs16
-rw-r--r--src/ext/framework/create_command.rs27
-rw-r--r--src/ext/framework/create_group.rs16
-rw-r--r--src/ext/framework/help_commands.rs120
-rw-r--r--src/ext/framework/mod.rs40
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));
}