dyncord 0.13.6

A high-level, ergonomic, batteries-included Discord bot library for Rust. WIP.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
//! Dyncord prefixed and slash commands.
//!
//! This module is divided into three command sub-modules, [`prefixed`], [`slash`], and
//! [`message`].
//!
//! - [`prefixed`] - Good old prefixed commands. E.g. `!help`.
//! - [`slash`] - Brand new slash commands. E.g. `/help`.
//! - [`message`] - Message commands. E.g. right click on a message > Apps > Help.
//!
//! Dyncord allows you to use both in the same bot, registration and routing is done automatically.
//!
//! This module also has command type-indifferent modules, like [`errors`] and [`permissions`].
//! Check their documentation out to learn about error types and permission checking.
//! 
//! The sections below are quick starts. Read the command type-specific submodule to learn about
//! the details of each command type and their supported features. Command type-indifferent modules
//! like [`errors`] and [`permissions`] aren't documented in command type-specific modules, so
//! check each of them's documentation to learn about those features.
//!
//! # Prefixed Commands
//!
//! Prefixed commands are message-based and are executed when the user sends a prefix plus the
//! command name. For example, where the prefix is `!`, the command's name is `hello`, and it takes
//! a string argument, users can invoke the command by sending `!hello Mike` in a channel the bot
//! has access to.
//!
//! To define such commands, create an async function that takes
//! [`PrefixedContext`](prefixed::context::PrefixedContext) as its first argument. For example, to
//! build our `hello` command mentioned in the example above, the function would be
//!
//! ```
//! async fn handle_hello(ctx: PrefixedContext) {}
//! ```
//!
//! That command took one string argument, so let's make it an argument in the function too.
//!
//! ```
//! async fn handle_hello(ctx: PrefixedContext, name: String) {}
//! ```
//!
//! Now, let's register it in our bot
//!
//! ```
//! let bot = Bot::new(())
//!     .intents(Intents::MESSAGE_CONTENT)
//!     .intents(Intents::GUILD_MESSAGES)
//!     .with_prefix("!")
//!     .command(Command::prefixed("hello", handle_hello));
//!
//! bot.run("token").await;
//! ```
//!
//! Great! Our bot now has a hello command. Try inviting the bot to your server and you'll see the
//! bot comes online when you run your binary. The command is invoked by sending `!hello yourname`.
//! However, no matter what you send, the bot does nothing. It's still correctly handling your
//! command, it just wasn't told to do anything when that happens yet. Let's do that.
//!
//! In your command handler function, add the following:
//!
//! ```
//! async fn handle_hello(ctx: PrefixedContext, name: String) {
//!     ctx.send(format!("Hey there, {name}!")).await.unwrap();
//! }
//! ```
//!
//! Try re-running your bot, then send `!hello yourname` in your server. VoilĂ ! The bot responded
//! according to the `handle_hello` function. Well done!
//!
//! You may have realized that if you pass more than a single word to the command, it just says hi
//! back with the first word. By default, [`String`] arguments take one word as an argument.
//! However, dyncord will also take more than one word as the argument if you quote the argument.
//! Try sending `!hello "Mike Wazowski"`. It works! What about single quotes?
//! `!hello 'Mike Wazowski'` and it also works! `!hello 'Mike\'s Cat'` and woah? Guess what, it
//! also works!
//!
//! For a more in depth introduction into prefixed commands, or just as a reference, check the
//! [`prefixed` module's documentation](prefixed). It has a more in depth guide about how to use
//! prefixed commands to their full power. Good luck!
//!
//! # Slash Commands
//!
//! Slash commands are more structured API-wise than prefixed commands. This is due to Discord's
//! need to know about every command, command group, and command argument in advance. The creation
//! of slash commands is almost as simple as the creation of prefixed commands, and type-safe so
//! that if any metadata is missing, you'll get an error at compile time instead of at runtime.
//!
//! Like prefixed commands, slash commands take a first required argument called the context. It
//! contains data about the command's invocation, together with metadata about the command itself
//! and functions to respond to the interaction. Such context type is
//! [`SlashContext`](slash::context::SlashContext).
//!
//! To create your first slash command handler, create an async function that takes
//! [`SlashContext`](slash::context::SlashContext) as its first argument.
//!
//! ```
//! async fn handle_hello(ctx: SlashContext) {
//!     ctx.respond("Hey there!").await.unwrap();
//! }
//! ```
//!
//! Let's pass it to our [`Bot`](crate::Bot).
//!
//! ```
//! let bot = Bot::new(()).command(Command::slash("hello", handle_hello));
//!
//! bot.run("token").await;
//! ```
//!
//! Great. Run your binary, get on Discord, and you'll see the bot is online and when you type
//! `/hello` your new command appears. Try running it!
//!
//! Now, let's add an argument to it, because right now our command doesn't know who to say hi to.
//! Let's add a name argument.
//!
//! ```
//! async fn handle_hello(ctx: SlashContext, name: String) {
//!     ctx.respond(format!("Hey {name}!")).await.unwrap();
//! }
//! ```
//!
//! Perfect. Try to run it and now... it fails to run. Why?
//!
//! Remember that some paragraphs ago, we said Discord needs some more information when creating a
//! command than just the argument name in the handler. If dyncord were to send the command as-is,
//! Discord would fail to register it because it would be lacking metadata, and therefore dyncord
//! fails early.
//!
//! Lets fix our error by passing metadata about our `name` argument to our [`Command`] builder.
//! Argument (options, as Discord calls them) metadata is defined through
//! [`Argument`](slash::arguments::Argument). In this case, we want to create a string argument so
//! we'll use [`Argument::string()`](slash::arguments::Argument::string).
//!
//! ```
//! let bot = Bot::new(()).command(
//!     Command::slash("hello", handle_hello)
//!         .argument(Argument::string("name"))
//! );
//! ```
//!
//! That's it! Go back to Discord and check your command. When you type `/hello` in your message
//! bar, you'll see that it now shows one option called "name". Perfect.
//!
//! You will see that the command's summary is "A Dyncord command." and the argument's summary is
//! "A Dyncord argument." To change those defaults, both the command builder and the argument
//! builder have `.description()` associated functions you can call.
//!
//! For example,
//!
//! ```
//! let bot = Bot::new(()).command(
//!     Command::slash("hello", handle_hello)
//!         .description("Says hi to someone.")
//!         .argument(
//!             Argument::string("name")
//!                 .description("Your name, to sell it to *ahem* to say hi.")
//!         )
//! );
//! ```
//!
//! If you run your binary again, you'll see that after some seconds the descriptions get updated.
//!
//! Last but not least, there's always the need for optional arguments. However, you'll see that
//! just passing `Option<String>` to your command handler as an argument will fail to run once
//! again. You have to mark your argument as optional by calling `.optional()` on it.
//!
//! This is only a short introduction to building slash commands with Dyncord. For a more extensive
//! documentation on how to implement them, check out the [`slash` module's documentation](slash).
//! It has all the details you'll need to be able to create slash commands more in detail. For now,
//! happy coding!
//! 
//! # Message Commands
//! 
//! Message commands are one of the simplest command types there are to work with in Discord.
//! They're shown as an option of a message's context menu<sup>1</sup>, don't require a
//! description, and don't take arguments other than the message they were called on.
//! 
//! > <sup>1</sup> Context menus are the pop-ups that appear with multiple options when you right
//! > click on a message on desktop, or when you press and hold a message on mobile. [Click here
//! > to see a screenshot of one](https://files.catbox.moe/5azefu.png).
//! 
//! Message commands, like all the other command types, are handled with an asynchronous function.
//! However, message command handlers don't take custom arguments. All message command handlers
//! must look like follows:
//! 
//! ```
//! async fn handle_message_command(ctx: MessageContext, message: Message) {}
//! ```
//! 
//! No custom arguments; Any handler signature that doesn't take those arguments that won't
//! compile.
//! 
//! To register a message command on your bot, call [`Bot::command`](crate::Bot::command) on your
//! bot. In essence,
//! 
//! ```
//! let bot = Bot::new(()).command(Command::message("Name", handle_message_command));
//! ``` 
//! 
//! Running the bot will automatically register your command and route to it when it gets called.
//! 
//! Like slash commands, you have to respond to the user interaction for Discord not to show an
//! error to the user when running your command. That's done with the
//! [`MessageContext`](message::context::MessageContext) argument your command handler takes.
//! 
//! [`MessageContext`](message::context::MessageContext) has two associated functions you can use
//! when responding to a command,
//! [`MessageContext::respond`](message::context::MessageContext::respond) and
//! [`MessageContext::respond`](message::context::MessageContext::defer).
//! 
//! - [`MessageContext::respond`](message::context::MessageContext::respond): Responds to the
//!   command call with a message. The most direct when your command does something quick.
//! - [`MessageContext::defer`](message::context::MessageContext::defer): Defers the response and
//!   shows a loading message to the user while you do some slower work. Call
//!   [`MessageContext::respond`](message::context::MessageContext::respond) when you're done doing
//!   the slower work.
//! 
//! For example,
//! 
//! ```
//! async fn handle_quick(ctx: MessageContext, message: Message) -> Result<(), TwilightError> {
//!     ctx.respond("Hey there!").await?;
//! 
//!     Ok(())
//! }
//! 
//! async fn handle_slow(ctx: MessageContext, message: Message) -> Result<(), TwilightError> {
//!     ctx.defer().await?;
//! 
//!     tokio::time::sleep(Duration::from_secs(3)).await;
//! 
//!     ctx.respond("Hey there!").await?;
//! 
//!     Ok(())
//! }
//! ```
//! 
//! This is only a short introduction to building message commands with Dyncord. For a more
//! extensive documentation on how to implement them, check out the
//! [`message` module's documentation](message). It has all the details you'll need to be able to
//! create message commands more in detail. For now, happy coding!

pub mod errors;
pub mod message;
pub mod permissions;
pub mod prefixed;
pub(crate) mod registration;
pub mod slash;

use crate::commands::errors::CommandError;
use crate::commands::message::{
    MessageCommand, MessageCommandBuilder, MessageCommandGroup, MessageCommandGroupBuilder,
    MessageCommandHandler,
};
use crate::commands::prefixed::{
    PrefixedCommand, PrefixedCommandBuilder, PrefixedCommandGroup, PrefixedCommandGroupBuilder,
    PrefixedCommandHandler,
};
use crate::commands::slash::{
    SlashCommand, SlashCommandBuilder, SlashCommandGroup, SlashCommandGroupBuilder,
    SlashCommandHandler,
};
use crate::state::StateBound;

/// Either a command or a command group.
#[derive(Clone)]
pub enum CommandNode<State>
where
    State: StateBound,
{
    PrefixedCommand(PrefixedCommand<State>),
    PrefixedCommandGroup(PrefixedCommandGroup<State>),
    SlashCommand(SlashCommand<State>),
    SlashCommandGroup(SlashCommandGroup<State>),
    MessageCommand(MessageCommand<State>),
    MessageCommandGroup(MessageCommandGroup<State>),
}

/// Converts all command types and their builder types into [`CommandNode`]s.
pub trait CommandIntoCommandNode<State>
where
    State: StateBound,
{
    /// Converts the current type into a [`CommandNode`].
    ///
    /// Returns:
    /// [`CommandNode`] - The resulting command node.
    fn into_command_node(self) -> CommandNode<State>;
}

impl<State> CommandIntoCommandNode<State> for PrefixedCommand<State>
where
    State: StateBound,
{
    fn into_command_node(self) -> CommandNode<State> {
        CommandNode::PrefixedCommand(self)
    }
}

impl<State> CommandIntoCommandNode<State> for PrefixedCommandBuilder<State>
where
    State: StateBound,
{
    fn into_command_node(self) -> CommandNode<State> {
        CommandNode::PrefixedCommand(self.build())
    }
}

impl<State> CommandIntoCommandNode<State> for SlashCommand<State>
where
    State: StateBound,
{
    fn into_command_node(self) -> CommandNode<State> {
        CommandNode::SlashCommand(self)
    }
}

impl<State> CommandIntoCommandNode<State> for SlashCommandBuilder<State>
where
    State: StateBound,
{
    fn into_command_node(self) -> CommandNode<State> {
        CommandNode::SlashCommand(self.build())
    }
}

impl<State> CommandIntoCommandNode<State> for MessageCommand<State>
where
    State: StateBound,
{
    fn into_command_node(self) -> CommandNode<State> {
        CommandNode::MessageCommand(self)
    }
}

impl<State> CommandIntoCommandNode<State> for MessageCommandBuilder<State>
where
    State: StateBound,
{
    fn into_command_node(self) -> CommandNode<State> {
        CommandNode::MessageCommand(self.build())
    }
}

/// A unified API to build commands.
///
/// This type's associated functions initialize specialized command types depending on what type is
/// being initialized. This type does not represent a command directly.
pub struct Command;

impl Command {
    /// Creates a new prefixed command builder with the given name and handler.
    ///
    /// Arguments:
    /// * `name` - The command's name, used to invoke the command.
    /// * `handler` - The command's handler, the function that executes when the command is run.
    ///
    /// Returns:
    /// [`PrefixedCommandBuilder`] - A new command builder with the given name and handler.
    pub fn prefixed<State, F, Args>(
        name: impl Into<String>,
        handler: F,
    ) -> PrefixedCommandBuilder<State>
    where
        F: PrefixedCommandHandler<State, Args> + 'static,
        Args: Send + Sync + 'static,
        State: StateBound,
    {
        PrefixedCommandBuilder::new(name, handler)
    }

    /// Creates a new slash command builder with the given name and handler.
    ///
    /// Arguments:
    /// * `name` - The command's name, used to invoke the command.
    /// * `handler` - The command's handler, the function that executes when the command is run.
    ///
    /// Returns:
    /// [`SlashCommandBuilder`] - A new slash command builder with the given name.
    pub fn slash<State, F, Args>(name: impl Into<String>, handler: F) -> SlashCommandBuilder<State>
    where
        F: SlashCommandHandler<State, Args> + 'static,
        State: StateBound,
        Args: Send + Sync + 'static,
    {
        SlashCommandBuilder::new(name.into(), handler)
    }

    /// Creates a new message command builder with the given name and handler.
    ///
    /// Arguments:
    /// * `name` - The command's name, shown to the user.
    /// * `handler` - The command's handler, the function that executes when the command is run.
    ///
    /// Returns:
    /// [`MessageCommandBuilder`] - A new message command builder with the given name.
    pub fn message<State, F>(name: impl Into<String>, handler: F) -> MessageCommandBuilder<State>
    where
        F: MessageCommandHandler<State> + 'static,
        State: StateBound,
    {
        MessageCommandBuilder::new(name.into(), handler)
    }
}

/// A unified API to build command groups.
///
/// This type's associated functions initialize specialized command group types depending on what
/// type is being initialized. This type does not represent a command group directly.
pub struct CommandGroup;

impl CommandGroup {
    /// Intializes a prefixed command group builder.
    ///
    /// Arguments:
    /// * `name` - The command group's name.
    ///
    /// Returns:
    /// [`PrefixedCommandGroupBuilder`] - A new prefixed command group builder.
    pub fn prefixed<State>(name: impl Into<String>) -> PrefixedCommandGroupBuilder<State>
    where
        State: StateBound,
    {
        PrefixedCommandGroupBuilder::new(name)
    }

    /// Intializes a slash command group builder.
    ///
    /// Arguments:
    /// * `name` - The command group's name.
    ///
    /// Returns:
    /// [`SlashCommandGroupBuilder`] - A new slash command group builder.
    pub fn slash<State>(name: impl Into<String>) -> SlashCommandGroupBuilder<State>
    where
        State: StateBound,
    {
        SlashCommandGroupBuilder::new(name)
    }

    /// Intializes a message command group builder.
    ///
    /// Arguments:
    /// * `name` - The command group's name.
    ///
    /// Returns:
    /// [`MessageCommandGroupBuilder`] - A new message command group builder.
    pub fn message<State>(name: impl Into<String>) -> MessageCommandGroupBuilder<State>
    where
        State: StateBound,
    {
        MessageCommandGroupBuilder::new(name)
    }
}

/// Converts all command group types and their builder types into a command node.
pub trait CommandGroupIntoCommandNode<State>
where
    State: StateBound,
{
    /// Converts the current type into a [`CommandNode`].
    ///
    /// Returns:
    /// [`CommandNode`] - The resulting command node.
    fn into_command_node(self) -> CommandNode<State>;
}

/// Flattens a [`CommandNode`] tree into a list of [`PrefixedCommand`]s.
///
/// Arguments:
/// * `nodes` - The nodes to flatten, which is a list of commands and command groups.
///
/// Returns:
/// [`Vec<PrefixedCommand>`] - A list of all the commands in the tree.
pub fn flatten_prefixed<State>(nodes: &[CommandNode<State>]) -> Vec<&PrefixedCommand<State>>
where
    State: StateBound,
{
    let mut commands = Vec::new();

    for node in nodes {
        match node {
            CommandNode::PrefixedCommand(command) => commands.push(command),
            CommandNode::PrefixedCommandGroup(group) => {
                commands.extend(flatten_prefixed(&group.children))
            }
            _ => {}
        }
    }

    commands
}

/// Flattens a [`CommandNode`] tree into a list of [`SlashCommand`]s.
///
/// Arguments:
/// * `nodes` - The nodes to flatten, which is a list of commands and command groups.
///
/// Returns:
/// [`Vec<SlashCommand>`] - A list of all the commands in the tree.
pub fn flatten_slash<State>(nodes: &[CommandNode<State>]) -> Vec<&SlashCommand<State>>
where
    State: StateBound,
{
    let mut commands = Vec::new();

    for node in nodes {
        match node {
            CommandNode::SlashCommand(command) => commands.push(command),
            CommandNode::PrefixedCommandGroup(group) => {
                commands.extend(flatten_slash(&group.children))
            }
            _ => {}
        }
    }

    commands
}

/// Flattens a [`CommandNode`] tree into a list of [`MessageCommand`]s.
///
/// Arguments:
/// * `nodes` - The nodes to flatten, which is a list of commands and command groups.
///
/// Returns:
/// [`Vec<MessageCommand>`] - A list of all the commands in the tree.
pub fn flatten_message<State>(nodes: &[CommandNode<State>]) -> Vec<&MessageCommand<State>>
where
    State: StateBound,
{
    let mut commands = Vec::new();

    for node in nodes {
        match node {
            CommandNode::MessageCommand(command) => commands.push(command),
            CommandNode::MessageCommandGroup(group) => {
                commands.extend(flatten_message(&group.children))
            }
            _ => {}
        }
    }

    commands
}

/// Returns all the prefixed commands in a list of [`CommandNode`]s.
///
/// Sub-commands inside command groups are not returned.
///
/// Arguments:
/// * `nodes` - The nodes to get the commands from, which is a list of commands and command groups.
///
/// Returns:
/// [`Vec<SlashCommand>`] - A list of all the prefixed commands in the list of nodes, excluding
/// sub-commands in command groups.
pub fn get_prefixed_commands<State>(nodes: &[CommandNode<State>]) -> Vec<&PrefixedCommand<State>>
where
    State: StateBound,
{
    let mut commands = Vec::new();

    for node in nodes {
        if let CommandNode::PrefixedCommand(command) = node {
            commands.push(command);
        }
    }

    commands
}

/// Returns all the slash commands in a list of [`CommandNode`]s.
///
/// Sub-commands inside command groups are not returned.
///
/// Arguments:
/// * `nodes` - The nodes to get the commands from, which is a list of commands and command groups.
///
/// Returns:
/// [`Vec<SlashCommand>`] - A list of all the slash commands in the list of nodes, excluding
/// sub-commands in command groups.
pub fn get_slash_commands<State>(nodes: &[CommandNode<State>]) -> Vec<&SlashCommand<State>>
where
    State: StateBound,
{
    let mut commands = Vec::new();

    for node in nodes {
        if let CommandNode::SlashCommand(command) = node {
            commands.push(command);
        }
    }

    commands
}

/// Returns all the message commands in a list of [`CommandNode`]s.
///
/// Sub-commands inside command groups are not returned.
///
/// Arguments:
/// * `nodes` - The nodes to get the commands from, which is a list of commands and command groups.
///
/// Returns:
/// [`Vec<MessageCommand>`] - A list of all the message commands in the list of nodes, excluding
/// sub-commands in command groups.
pub fn get_message_commands<State>(nodes: &[CommandNode<State>]) -> Vec<&MessageCommand<State>>
where
    State: StateBound,
{
    let mut commands = Vec::new();

    for node in nodes {
        if let CommandNode::MessageCommand(command) = node {
            commands.push(command);
        }
    }

    commands
}

/// Returns all the prefixed command groups in a list of [`CommandNode`]s.
///
/// Sub-groups inside command groups are not returned.
///
/// Arguments:
/// * `nodes` - The nodes to get the command groups from, which is a list of commands and command
///   groups.
///
/// Returns:
/// [`Vec<PrefixedCommandGroup>`] - A list of all the command groups in the list of nodes,
/// excluding sub-groups in command groups.
pub fn get_prefixed_groups<State>(nodes: &[CommandNode<State>]) -> Vec<&PrefixedCommandGroup<State>>
where
    State: StateBound,
{
    let mut groups = Vec::new();

    for node in nodes {
        if let CommandNode::PrefixedCommandGroup(group) = node {
            groups.push(group);
        }
    }

    groups
}

/// Returns all the slash command groups in a list of [`CommandNode`]s.
///
/// Sub-groups inside command groups are not returned.
///
/// Arguments:
/// * `nodes` - The nodes to get the command groups from, which is a list of commands and command
///   groups.
///
/// Returns:
/// [`Vec<SlashCommandGroup>`] - A list of all the slash command groups in the list of nodes,
/// excluding sub-groups in command groups.
pub fn get_slash_groups<State>(nodes: &[CommandNode<State>]) -> Vec<&SlashCommandGroup<State>>
where
    State: StateBound,
{
    let mut groups = Vec::new();

    for node in nodes {
        if let CommandNode::SlashCommandGroup(group) = node {
            groups.push(group);
        }
    }

    groups
}

/// Returns all the message command groups in a list of [`CommandNode`]s.
///
/// Sub-groups inside command groups are not returned.
///
/// Arguments:
/// * `nodes` - The nodes to get the command groups from, which is a list of commands and command
///   groups.
///
/// Returns:
/// [`Vec<MessageCommandGroup>`] - A list of all the message command groups in the list of nodes,
/// excluding sub-groups in command groups.
pub fn get_message_groups<State>(nodes: &[CommandNode<State>]) -> Vec<&MessageCommandGroup<State>>
where
    State: StateBound,
{
    let mut groups = Vec::new();

    for node in nodes {
        if let CommandNode::MessageCommandGroup(group) = node {
            groups.push(group);
        }
    }

    groups
}

/// The result of running a command.
pub type CommandResult = Result<(), CommandError>;