Rust_Discord_API/
router.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3use tokio::sync::RwLock;
4use async_trait::async_trait;
5use reqwest::Client;
6use std::error::Error;
7
8#[async_trait]
9/// The `Command` trait defines a common interface for all commands.
10/// Each command must implement the `execute` method which handles the command's logic.
11pub trait Command: Send + Sync {
12    /// Execute the command.
13    ///
14    /// # Arguments
15    ///
16    /// * `client` - The HTTP client used to send requests.
17    /// * `token` - The bot token for authentication.
18    /// * `channel_id` - The ID of the channel where the command was invoked.
19    /// * `args` - The arguments passed to the command.
20    ///
21    /// # Returns
22    ///
23    /// A result indicating success or failure.
24    async fn execute(&self, client: &Client, token: &str, channel_id: &str, args: &str) -> Result<(), Box<dyn Error>>;
25}
26
27/// The `CommandRouter` struct is responsible for managing and dispatching commands.
28pub struct CommandRouter {
29    commands: HashMap<String, Arc<dyn Command>>,
30}
31
32impl CommandRouter {
33    /// Create a new `CommandRouter`.
34    ///
35    /// # Examples
36    ///
37    /// ```
38    /// use exampleapp::CommandRouter;
39    ///
40    /// let command_router = CommandRouter::new();
41    /// ```
42    pub fn new() -> Self {
43        Self {
44            commands: HashMap::new(),
45        }
46    }
47
48    /// Register a command with the router.
49    ///
50    /// # Arguments
51    ///
52    /// * `name` - The name of the command (e.g., "!ping").
53    /// * `command` - The command to register.
54    ///
55    /// # Examples
56    ///
57    /// ```
58    /// use exampleapp::{CommandRouter, Command};
59    /// use std::sync::Arc;
60    /// use async_trait::async_trait;
61    /// use reqwest::Client;
62    /// use std::error::Error;
63    ///
64    /// struct PingCommand;
65    ///
66    /// #[async_trait]
67    /// impl Command for PingCommand {
68    ///     async fn execute(&self, client: &Client, token: &str, channel_id: &str, args: &str) -> Result<(), Box<dyn Error>> {
69    ///         println!("Pong!");
70    ///         Ok(())
71    ///     }
72    /// }
73    ///
74    /// let mut command_router = CommandRouter::new();
75    /// command_router.register_command("!ping", Arc::new(PingCommand));
76    /// ```
77    pub fn register_command(&mut self, name: &str, command: Arc<dyn Command>) {
78        self.commands.insert(name.to_string(), command);
79    }
80
81    /// Dispatch a command based on the input content.
82    ///
83    /// # Arguments
84    ///
85    /// * `client` - The HTTP client used to send requests.
86    /// * `token` - The bot token for authentication.
87    /// * `channel_id` - The ID of the channel where the command was invoked.
88    /// * `content` - The content of the message.
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// use exampleapp::{CommandRouter, Command};
94    /// use std::sync::Arc;
95    /// use tokio::sync::RwLock;
96    /// use reqwest::Client;
97    /// use std::env;
98    /// use async_trait::async_trait;
99    /// use std::error::Error;
100    ///
101    /// #[tokio::main]
102    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
103    ///     let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
104    ///
105    ///     let client = Client::new();
106    ///     let mut command_router = CommandRouter::new();
107    ///
108    ///     struct PingCommand;
109    ///
110    ///     #[async_trait]
111    ///     impl Command for PingCommand {
112    ///         async fn execute(&self, client: &Client, token: &str, channel_id: &str, args: &str) -> Result<(), Box<dyn Error>> {
113    ///             println!("Pong!");
114    ///             Ok(())
115    ///         }
116    ///     }
117    ///
118    ///     command_router.register_command("!ping", Arc::new(PingCommand));
119    ///
120    ///     let command_router = Arc::new(RwLock::new(command_router));
121    ///
122    ///     let simulated_messages = vec![
123    ///         ("!ping", "channel_id_1"),
124    ///     ];
125    ///
126    ///     for (content, channel_id) in simulated_messages {
127    ///         let router = command_router.read().await;
128    ///         router.dispatch(&client, &token, channel_id, content).await?;
129    ///     }
130    ///
131    ///     Ok(())
132    /// }
133    /// ```
134    pub async fn dispatch(&self, client: &Client, token: &str, channel_id: &str, content: &str) -> Result<(), Box<dyn Error>> {
135        let parts: Vec<&str> = content.splitn(2, ' ').collect();
136        let command_name = parts[0];
137        let args = if parts.len() > 1 { parts[1] } else { "" };
138
139        if let Some(command) = self.commands.get(command_name) {
140            command.execute(client, token, channel_id, args).await?;
141        } else {
142            println!("Command not found: {}", command_name);
143        }
144
145        Ok(())
146    }
147}