use crate::validators; use mirror_core::{conversion_err, validation_error}; use mlua::{FromLua, Lua, Value}; use std::collections::HashMap; const ALLOWED_KEYS: &[&str] = &["handler", "description"]; #[derive(Debug, Default, Clone)] pub struct MrCommand { pub commands: HashMap, } impl MrCommand { pub fn get(&self, name: &str) -> Option<&MrSubCommand> { self.commands.get(name) } } impl FromLua for MrCommand { fn from_lua(value: Value, lua: &Lua) -> mlua::Result { let root_tbl = match value { Value::Table(t) => t, other => { return Err(conversion_err( other.type_name(), "MrCommand", format!( "commands 注册表的顶层配置必须是 Table,实际检测到: {}", other.type_name() ), )); } }; let mut commands = HashMap::new(); for pair in root_tbl.pairs::() { let (cmd_name, cmd_entry) = pair?; let cmd_name = validators::validate_command_name(&cmd_name)?; // 2. 校验 entry 是否是 Table if !cmd_entry.is_table() { return Err(conversion_err( cmd_entry.type_name(), "MrCommand", format!( "子命令 '{cmd_name}' 的配置必须是 Table,实际检测到: {}", cmd_entry.type_name() ), )); } // 3. 直接交由子命令解析,外层负责补充错误上下文 let cmd_def = MrSubCommand::from_lua(cmd_entry, lua).map_err(|err| { validation_error(format!("子命令 '{cmd_name}' 配置解析失败:\n{err}")) })?; commands.insert(cmd_name, cmd_def); } Ok(Self { commands }) } } #[derive(Debug, Default, Clone)] pub struct MrSubCommand { /// 命令处理函数:必须是 Lua Function pub(crate) module: String, /// 命令行说明:必须是普通文本字符串 pub(crate) description: String, } impl FromLua for MrSubCommand { fn from_lua(value: Value, _lua: &Lua) -> mlua::Result { let table = match value { Value::Table(t) => t, other => { return Err(conversion_err( other.type_name(), "MrSubCommand", format!("配置必须是 Table,实际检测到: {}", other.type_name()), )); } }; // validators::LuaValidator::parse_command_entry(&tbl) for pair in table.pairs::() { let (key, _) = pair?; let key_str = validators::validate_command_name(&key)?; if !ALLOWED_KEYS.contains(&key_str.as_str()) { return Err(validation_error(format!( "包含未知字段 '{key_str}'。\n\ ==> 合法字段仅支持: {}\n\ ==> 请检查是否存在拼写手误(例如把 handler 写成了 handle/handlr)", ALLOWED_KEYS.join(", ") ))); } } let module_val = table.get::("module")?; // let handler_func = match handler_val { // Value::Function(f) => f, // Value::Boolean(true) => { // return Err(validation_error( // "子命令 handler 返回了布尔值 true。\n\ // ==>排查提示:脚本已成功加载,但未返回函数。\n\ // ==>请在末尾加上 'return function(args) ... end'。", // )); // } // Value::Nil => { // return Err(validation_error( // "子命令缺少必填字段 handler(或对应脚本未返回函数)", // )); // } // other => { // return Err(validation_error(format!( // "'handler' 必须是函数 (function),实际类型是: {}", // other.type_name() // ))); // } // }; let module = match module_val { Value::String(s) => { // 兜底归一化(防止用户手写 module = "a/b" 时未走 mr.command) // 同时兼容直接写字符串的情况 s.to_str()?.replace(['/', '\\'], ".") } other => { return Err(mlua::Error::runtime(format!( "module 必须通过 mr.command(\"xxx\") 声明,实际为 {}", other.type_name() ))); } }; // 可选字段: args(缺失或 nil 时默认空列表;保留空字符串参数,与提权路径的 "" 语义一致) let description_val = table.get::("description")?; let description = match description_val { Value::Nil => String::new(), Value::String(s) => s.to_str()?.to_string(), other => { return Err(validation_error(format!( "必须是字符串文本,实际类型是 {}", other.type_name() ))); } }; // let handler = lua.create_registry_value(handler_func)?; Ok(Self { module, description, }) } }