use crate::context::filter::ContextFilter;
use crate::error::FrameworkResult;
use crate::session::Session;
use std::collections::HashMap;
use std::fmt::Debug;
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, RwLock};
#[derive(Debug, Clone, Default)]
pub struct ParsedArgs {
pub arguments: Vec<String>,
pub options: HashMap<String, String>, }
pub type CommandAction = Box<
dyn Fn(
Arc<Session>,
ParsedArgs,
) -> Pin<Box<dyn Future<Output = FrameworkResult<()>> + Send + Sync>>
+ Send
+ Sync,
>;
pub struct Command {
pub name: String,
pub aliases: Vec<String>,
pub description: Option<String>,
pub filter: ContextFilter,
pub action: CommandAction,
}
impl Debug for Command {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Command")
.field("name", &self.name)
.field("aliases", &self.aliases)
.field("description", &self.description)
.field("filter", &self.filter)
.field("action", &"Box<dyn Fn(...)>") .finish()
}
}
#[derive(Default, Debug)]
pub struct CommandRegistry {
pub commands: HashMap<String, Arc<Command>>,
}
impl CommandRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, command: Command) -> FrameworkResult<()> {
let command_arc = Arc::new(command);
if self.commands.contains_key(&command_arc.name) {
tracing::warn!("指令 {} 已注册,将被覆盖。", command_arc.name);
}
self.commands
.insert(command_arc.name.clone(), Arc::clone(&command_arc));
for alias in &command_arc.aliases {
if self.commands.contains_key(alias) {
tracing::warn!(
"指令 {} 的别名 {} 已注册或与另一指令冲突,将被覆盖。",
command_arc.name,
alias
);
}
self.commands
.insert(alias.clone(), Arc::clone(&command_arc));
}
Ok(())
}
pub async fn parse_and_execute(
&self,
session: Arc<Session>,
message_content: &str,
prefixes: &[&str], ) -> FrameworkResult<bool> {
let mut potential_command_text: Option<&str> = None;
for prefix in prefixes {
if message_content.starts_with(prefix) {
potential_command_text =
Some(message_content.trim_start_matches(prefix).trim_start());
break;
}
}
if potential_command_text.is_none() {
return Ok(false); }
let text = potential_command_text.unwrap();
if text.is_empty() {
return Ok(false); }
let parts: Vec<&str> = text.split_whitespace().collect();
let command_name = parts[0];
if let Some(command_arc) = self.commands.get(command_name) {
if !command_arc.filter.matches_session(&session) {
tracing::trace!(
"指令 {} 找到,但其上下文过滤器不匹配当前会话。",
command_name
);
return Ok(false); }
tracing::debug!("正在执行指令: {}", command_name);
let mut parsed_args = ParsedArgs::default();
let mut i = 1; while i < parts.len() {
let part = parts[i];
if part.starts_with("--") {
let option_name = part.trim_start_matches("--").to_string();
if i + 1 < parts.len() {
let next_part = parts[i + 1];
if !next_part.starts_with('-') && !next_part.is_empty() {
parsed_args
.options
.insert(option_name, next_part.to_string());
i += 1; } else {
parsed_args.options.insert(option_name, String::new());
}
} else {
parsed_args.options.insert(option_name, String::new());
}
} else if part.starts_with('-') && part.len() > 1 && !part.starts_with("--") {
for (idx, char_val) in part.char_indices() {
if idx == 0 {
continue;
}
parsed_args
.options
.insert(char_val.to_string(), String::new());
}
} else {
parsed_args.arguments.push(part.to_string());
}
i += 1;
}
(command_arc.action)(session, parsed_args).await?;
Ok(true) } else {
tracing::trace!("未知指令: {}", command_name);
Ok(false) }
}
}
pub struct CommandBuilder {
name: String,
aliases: Vec<String>,
description: Option<String>,
filter: ContextFilter, action: Option<CommandAction>,
registry: Arc<RwLock<CommandRegistry>>, }
impl CommandBuilder {
pub fn new(
name: String,
filter: ContextFilter,
registry: Arc<RwLock<CommandRegistry>>,
) -> Self {
CommandBuilder {
name,
aliases: Vec::new(),
description: None,
filter,
action: None,
registry,
}
}
pub fn alias(mut self, alias: &str) -> Self {
self.aliases.push(alias.to_string());
self
}
pub fn description(mut self, description: &str) -> Self {
self.description = Some(description.to_string());
self
}
pub fn action<F, Fut>(mut self, f: F) -> Self
where
F: Fn(Arc<Session>, ParsedArgs) -> Fut + Send + Sync + 'static,
Fut: Future<Output = FrameworkResult<()>> + Send + Sync + 'static,
{
self.action = Some(Box::new(move |session, args| Box::pin(f(session, args))));
self
}
pub fn register(self) -> FrameworkResult<()> {
let action = self.action.ok_or_else(|| {
crate::error::FrameworkError::Command(format!("指令 '{}' 没有定义 action", self.name))
})?;
let command = Command {
name: self.name.clone(),
aliases: self.aliases,
description: self.description,
filter: self.filter,
action,
};
let mut registry_guard = self.registry.write().map_err(|_| {
crate::error::FrameworkError::Internal("无法获取 CommandRegistry 的写锁".to_string())
})?;
registry_guard.register(command)?;
tracing::info!("指令 '{}' 已注册", self.name);
Ok(())
}
}