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}