serenity_self/framework/standard/structures/
mod.rs

1use std::collections::HashSet;
2use std::error::Error as StdError;
3use std::fmt;
4
5use futures::future::BoxFuture;
6
7use super::Args;
8use crate::client::Context;
9use crate::model::channel::Message;
10use crate::model::id::UserId;
11use crate::model::permissions::Permissions;
12use crate::model::Colour;
13
14pub mod buckets;
15mod check;
16
17pub use self::check::*;
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20#[non_exhaustive]
21pub enum OnlyIn {
22    Dm,
23    Guild,
24    None,
25}
26
27impl Default for OnlyIn {
28    fn default() -> Self {
29        Self::None
30    }
31}
32
33#[derive(Debug, Default, PartialEq)]
34pub struct CommandOptions {
35    /// A set of checks to be called prior to executing the command. The checks will short-circuit
36    /// on the first check that returns `false`.
37    pub checks: &'static [&'static Check],
38    /// Ratelimit bucket.
39    pub bucket: Option<&'static str>,
40    /// Names that the command can be referred to.
41    pub names: &'static [&'static str],
42    /// Command description, used by other commands.
43    pub desc: Option<&'static str>,
44    /// Delimiters used to split the arguments of the command by. If empty, the [global delimiters]
45    /// are used.
46    ///
47    /// [global delimiters]: super::Configuration::delimiters
48    pub delimiters: &'static [&'static str],
49    /// Command usage schema, used by other commands.
50    pub usage: Option<&'static str>,
51    /// Example arguments, used by other commands.
52    pub examples: &'static [&'static str],
53    /// Minimum amount of arguments that should be passed.
54    pub min_args: Option<u16>,
55    /// Maximum amount of arguments that can be passed.
56    pub max_args: Option<u16>,
57    /// Roles allowed to use this command.
58    pub allowed_roles: &'static [&'static str],
59    /// Permissions required to use this command.
60    pub required_permissions: Permissions,
61    /// Whether the command should be displayed in help list or not, used by other commands.
62    pub help_available: bool,
63    /// Whether the command can only be used in dms or guilds; or both.
64    pub only_in: OnlyIn,
65    /// Whether the command can only be used by owners or not.
66    pub owners_only: bool,
67    /// Whether the command treats owners as normal users.
68    pub owner_privilege: bool,
69    /// Other commands belonging to this command.
70    pub sub_commands: &'static [&'static Command],
71}
72
73pub type CommandError = Box<dyn StdError + Send + Sync>;
74pub type CommandResult<T = ()> = std::result::Result<T, CommandError>;
75pub type CommandFn =
76    for<'fut> fn(&'fut Context, &'fut Message, Args) -> BoxFuture<'fut, CommandResult>;
77
78pub struct Command {
79    pub fun: CommandFn,
80    pub options: &'static CommandOptions,
81}
82
83impl fmt::Debug for Command {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        f.debug_struct("Command").field("options", &self.options).finish_non_exhaustive()
86    }
87}
88
89impl PartialEq for Command {
90    #[inline]
91    fn eq(&self, other: &Command) -> bool {
92        (self.fun as usize == other.fun as usize) && (self.options == other.options)
93    }
94}
95
96pub type HelpCommandFn = for<'fut> fn(
97    &'fut Context,
98    &'fut Message,
99    Args,
100    &'fut HelpOptions,
101    &'fut [&'static CommandGroup],
102    HashSet<UserId>,
103) -> BoxFuture<'fut, CommandResult>;
104
105pub struct HelpCommand {
106    pub fun: HelpCommandFn,
107    pub options: &'static HelpOptions,
108}
109
110impl fmt::Debug for HelpCommand {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        f.debug_struct("HelpCommand")
113            .field("fun", &"<function>")
114            .field("options", &self.options)
115            .finish()
116    }
117}
118
119impl PartialEq for HelpCommand {
120    #[inline]
121    fn eq(&self, other: &HelpCommand) -> bool {
122        (self.fun as usize == other.fun as usize) && (self.options == other.options)
123    }
124}
125
126/// Describes the behaviour the help-command shall execute once it encounters a command which the
127/// user or command fails to meet following criteria :
128/// - Lacking required permissions to execute the command.
129/// - Lacking required roles to execute the command.
130/// - The command can't be used in the current channel (as in `DM only` or `guild only`).
131#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
132#[non_exhaustive]
133pub enum HelpBehaviour {
134    /// The command will be displayed, hence nothing will be done.
135    Nothing,
136    /// Strikes a command by applying `~~{command_name}~~`.
137    Strike,
138    /// Does not list a command in the help-menu.
139    Hide,
140}
141
142#[derive(Clone, Debug, PartialEq, Eq)]
143pub struct HelpOptions {
144    /// Which names should the help command use for dispatching.
145    /// Defaults to `["help"]`
146    pub names: &'static [&'static str],
147    /// Suggests a command's name.
148    pub suggestion_text: &'static str,
149    /// If no help is available, this text will be displayed.
150    pub no_help_available_text: &'static str,
151    /// How to use a command, `{usage_label}: {command_name} {args}`
152    pub usage_label: &'static str,
153    /// Actual sample label, `{usage_sample_label}: {command_name} {args}`
154    pub usage_sample_label: &'static str,
155    /// Text labeling ungrouped commands, `{ungrouped_label}: ...`
156    pub ungrouped_label: &'static str,
157    /// Text labeling the start of the description.
158    pub description_label: &'static str,
159    /// Text labeling grouped commands, `{grouped_label} {group_name}: ...`
160    pub grouped_label: &'static str,
161    /// Text labeling a command's alternative names (aliases).
162    pub aliases_label: &'static str,
163    /// Text specifying that a command is only usable in a guild.
164    pub guild_only_text: &'static str,
165    /// Text labelling a command's names of checks.
166    pub checks_label: &'static str,
167    /// Text labelling a command's subcommands
168    pub sub_commands_label: &'static str,
169    /// Text specifying that a command is only usable in via DM.
170    pub dm_only_text: &'static str,
171    /// Text specifying that a command can be used via DM and in guilds.
172    pub dm_and_guild_text: &'static str,
173    /// Text expressing that a command is available.
174    pub available_text: &'static str,
175    /// Error-message once a command could not be found.
176    ///
177    /// Output-example (without whitespace between both substitutions:
178    /// `{command_not_found_text}{command_name}` `{command_name}` describes user's input as in:
179    /// `{prefix}help {command_name}`.
180    pub command_not_found_text: &'static str,
181    /// Explains the user on how to use access a single command's details.
182    pub individual_command_tip: &'static str,
183    /// Explains reasoning behind strikethrough-commands, see fields requiring [`HelpBehaviour`]
184    /// for further information. If [`HelpBehaviour::Strike`] is unused, this field will evaluate
185    /// to [`None`] during creation inside of the help macro.
186    ///
187    /// **Note**: Text is only used in direct messages.
188    pub strikethrough_commands_tip_in_dm: Option<&'static str>,
189    /// Explains reasoning behind strikethrough-commands, see fields requiring [`HelpBehaviour`]
190    /// for further information. If [`HelpBehaviour::Strike`] is unused, this field will evaluate
191    /// to [`None`] during creation inside of the help macro.
192    ///
193    /// **Note**: Text is only used in guilds.
194    pub strikethrough_commands_tip_in_guild: Option<&'static str>,
195    /// Announcing a group's prefix as in: {group_prefix} {prefix}.
196    pub group_prefix: &'static str,
197    /// If a user lacks required roles, this will treat how these commands will be displayed.
198    pub lacking_role: HelpBehaviour,
199    /// If a user lacks permissions, this will treat how these commands will be displayed.
200    pub lacking_permissions: HelpBehaviour,
201    /// If a user lacks ownership, this will treat how these commands will be displayed.
202    pub lacking_ownership: HelpBehaviour,
203    /// If conditions (of a check) may be lacking by the user, this will treat how these commands
204    /// will be displayed.
205    pub lacking_conditions: HelpBehaviour,
206    /// If a user is using the help-command in a channel where a command is not available,
207    /// this behaviour will be executed.
208    pub wrong_channel: HelpBehaviour,
209    /// Colour help-embed will use upon encountering an error.
210    pub embed_error_colour: Colour,
211    /// Colour help-embed will use if no error occurred.
212    pub embed_success_colour: Colour,
213    /// If not 0, help will check whether a command is similar to searched named.
214    pub max_levenshtein_distance: usize,
215    /// Help will use this as prefix to express how deeply nested a command or
216    /// group is.
217    pub indention_prefix: &'static str,
218}
219
220#[derive(Debug, Default, PartialEq)]
221pub struct GroupOptions {
222    pub prefixes: &'static [&'static str],
223    pub only_in: OnlyIn,
224    pub owners_only: bool,
225    pub owner_privilege: bool,
226    pub help_available: bool,
227    pub allowed_roles: &'static [&'static str],
228    pub required_permissions: Permissions,
229    pub checks: &'static [&'static Check],
230    pub default_command: Option<&'static Command>,
231    pub description: Option<&'static str>,
232    pub summary: Option<&'static str>,
233    pub commands: &'static [&'static Command],
234    pub sub_groups: &'static [&'static CommandGroup],
235}
236
237#[derive(Debug, PartialEq)]
238pub struct CommandGroup {
239    pub name: &'static str,
240    pub options: &'static GroupOptions,
241}
242
243#[cfg(test)]
244#[cfg(all(feature = "cache", feature = "http"))]
245mod levenshtein_tests {
246    use super::HelpBehaviour;
247
248    #[test]
249    fn help_behaviour_eq() {
250        assert_eq!(HelpBehaviour::Hide, std::cmp::max(HelpBehaviour::Hide, HelpBehaviour::Hide));
251        assert_eq!(
252            HelpBehaviour::Strike,
253            std::cmp::max(HelpBehaviour::Strike, HelpBehaviour::Strike)
254        );
255        assert_eq!(
256            HelpBehaviour::Nothing,
257            std::cmp::max(HelpBehaviour::Nothing, HelpBehaviour::Nothing)
258        );
259    }
260
261    #[test]
262    fn help_behaviour_hide() {
263        assert_eq!(HelpBehaviour::Hide, std::cmp::max(HelpBehaviour::Hide, HelpBehaviour::Nothing));
264        assert_eq!(HelpBehaviour::Hide, std::cmp::max(HelpBehaviour::Hide, HelpBehaviour::Strike));
265    }
266
267    #[test]
268    fn help_behaviour_strike() {
269        assert_eq!(
270            HelpBehaviour::Strike,
271            std::cmp::max(HelpBehaviour::Strike, HelpBehaviour::Nothing)
272        );
273        assert_eq!(HelpBehaviour::Hide, std::cmp::max(HelpBehaviour::Strike, HelpBehaviour::Hide));
274    }
275
276    #[test]
277    fn help_behaviour_nothing() {
278        assert_eq!(
279            HelpBehaviour::Strike,
280            std::cmp::max(HelpBehaviour::Nothing, HelpBehaviour::Strike)
281        );
282        assert_eq!(HelpBehaviour::Hide, std::cmp::max(HelpBehaviour::Nothing, HelpBehaviour::Hide));
283    }
284}