1use crate::context::filter::ContextFilter;
2use crate::error::FrameworkResult;
3use crate::session::Session;
4use std::collections::HashMap;
5use std::fmt::Debug;
6use std::future::Future;
7use std::pin::Pin;
8use std::sync::{Arc, RwLock};
9
10#[derive(Debug, Clone, Default)]
12pub struct ParsedArgs {
13 pub arguments: Vec<String>,
15 pub options: HashMap<String, String>, }
19
20pub type CommandAction = Box<
22 dyn Fn(
23 Arc<Session>,
24 ParsedArgs,
25 ) -> Pin<Box<dyn Future<Output = FrameworkResult<()>> + Send + Sync>>
26 + Send
27 + Sync,
28>;
29
30pub struct Command {
32 pub name: String,
34 pub aliases: Vec<String>,
36 pub description: Option<String>,
38 pub filter: ContextFilter,
40 pub action: CommandAction,
42 }
46
47impl Debug for Command {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.debug_struct("Command")
50 .field("name", &self.name)
51 .field("aliases", &self.aliases)
52 .field("description", &self.description)
53 .field("filter", &self.filter)
54 .field("action", &"Box<dyn Fn(...)>") .finish()
56 }
57}
58
59#[derive(Default, Debug)]
61pub struct CommandRegistry {
62 pub commands: HashMap<String, Arc<Command>>,
64}
65
66impl CommandRegistry {
67 pub fn new() -> Self {
68 Self::default()
69 }
70
71 pub fn register(&mut self, command: Command) -> FrameworkResult<()> {
74 let command_arc = Arc::new(command);
75 if self.commands.contains_key(&command_arc.name) {
77 tracing::warn!("指令 {} 已注册,将被覆盖。", command_arc.name);
79 }
80 self.commands
81 .insert(command_arc.name.clone(), Arc::clone(&command_arc));
82
83 for alias in &command_arc.aliases {
85 if self.commands.contains_key(alias) {
86 tracing::warn!(
87 "指令 {} 的别名 {} 已注册或与另一指令冲突,将被覆盖。",
88 command_arc.name,
89 alias
90 );
91 }
92 self.commands
93 .insert(alias.clone(), Arc::clone(&command_arc));
94 }
95 Ok(())
96 }
97
98 pub async fn parse_and_execute(
112 &self,
113 session: Arc<Session>,
114 message_content: &str,
115 prefixes: &[&str], ) -> FrameworkResult<bool> {
118 let mut potential_command_text: Option<&str> = None;
119
120 for prefix in prefixes {
121 if message_content.starts_with(prefix) {
122 potential_command_text =
123 Some(message_content.trim_start_matches(prefix).trim_start());
124 break;
125 }
126 }
127
128 if potential_command_text.is_none() {
129 return Ok(false); }
131
132 let text = potential_command_text.unwrap();
133 if text.is_empty() {
134 return Ok(false); }
136
137 let parts: Vec<&str> = text.split_whitespace().collect();
138 let command_name = parts[0];
139
140 if let Some(command_arc) = self.commands.get(command_name) {
141 if !command_arc.filter.matches_session(&session) {
143 tracing::trace!(
144 "指令 {} 找到,但其上下文过滤器不匹配当前会话。",
145 command_name
146 );
147 return Ok(false); }
149
150 tracing::debug!("正在执行指令: {}", command_name);
151
152 let mut parsed_args = ParsedArgs::default();
153 let mut i = 1; while i < parts.len() {
155 let part = parts[i];
156 if part.starts_with("--") {
157 let option_name = part.trim_start_matches("--").to_string();
158 if i + 1 < parts.len() {
159 let next_part = parts[i + 1];
160 if !next_part.starts_with('-') && !next_part.is_empty() {
162 parsed_args
164 .options
165 .insert(option_name, next_part.to_string());
166 i += 1; } else {
168 parsed_args.options.insert(option_name, String::new());
170 }
171 } else {
172 parsed_args.options.insert(option_name, String::new());
174 }
175 } else if part.starts_with('-') && part.len() > 1 && !part.starts_with("--") {
176 for (idx, char_val) in part.char_indices() {
178 if idx == 0 {
179 continue;
180 }
181 parsed_args
182 .options
183 .insert(char_val.to_string(), String::new());
184 }
185 } else {
186 parsed_args.arguments.push(part.to_string());
188 }
189 i += 1;
190 }
191
192 (command_arc.action)(session, parsed_args).await?;
194 Ok(true) } else {
196 tracing::trace!("未知指令: {}", command_name);
197 Ok(false) }
199 }
200}
201
202pub struct CommandBuilder {
204 name: String,
205 aliases: Vec<String>,
206 description: Option<String>,
207 filter: ContextFilter, action: Option<CommandAction>,
209 registry: Arc<RwLock<CommandRegistry>>, }
211
212impl CommandBuilder {
213 pub fn new(
216 name: String,
217 filter: ContextFilter,
218 registry: Arc<RwLock<CommandRegistry>>,
219 ) -> Self {
220 CommandBuilder {
221 name,
222 aliases: Vec::new(),
223 description: None,
224 filter,
225 action: None,
226 registry,
227 }
228 }
229
230 pub fn alias(mut self, alias: &str) -> Self {
232 self.aliases.push(alias.to_string());
233 self
234 }
235
236 pub fn description(mut self, description: &str) -> Self {
238 self.description = Some(description.to_string());
239 self
240 }
241
242 pub fn action<F, Fut>(mut self, f: F) -> Self
244 where
245 F: Fn(Arc<Session>, ParsedArgs) -> Fut + Send + Sync + 'static,
246 Fut: Future<Output = FrameworkResult<()>> + Send + Sync + 'static,
247 {
248 self.action = Some(Box::new(move |session, args| Box::pin(f(session, args))));
249 self
250 }
251
252 pub fn register(self) -> FrameworkResult<()> {
254 let action = self.action.ok_or_else(|| {
255 crate::error::FrameworkError::Command(format!("指令 '{}' 没有定义 action", self.name))
256 })?;
257
258 let command = Command {
259 name: self.name.clone(),
260 aliases: self.aliases,
261 description: self.description,
262 filter: self.filter,
263 action,
264 };
265
266 let mut registry_guard = self.registry.write().map_err(|_| {
267 crate::error::FrameworkError::Internal("无法获取 CommandRegistry 的写锁".to_string())
268 })?;
269
270 registry_guard.register(command)?;
271 tracing::info!("指令 '{}' 已注册", self.name);
272 Ok(())
273 }
274}