Files
mirror/mirror-core/src/plugins.rs
T
CNWei 38dabd2ace refactor(core,shim): 统一基于 thiserror 的错误体系
- 移除 anyhow,使用 thiserror 定义强类型 Error 枚举
- 支持 #[from] 自动转换 mlua::Error,关键路径保留源码与上下文
- LuaRuntimeBuilder 采用惰性错误收集,简化链式初始化调用
- MirrorRegistry 实现规范化 FromLuaConversionError 错误转换
2026-09-10 14:33:54 +08:00

112 lines
3.9 KiB
Rust

use crate::error::{Error, Result};
use crate::{Layout, normalize_path_for_lua};
use mlua::{Lua, Table, Value};
use std::env;
use tracing::{debug, warn};
const MIRROR_DIR: &str = "__MIRROR_DIR__";
// 采用形式 A:直接传引用的普通函数
pub fn register_basics(lua: &Lua, layout: &Layout) -> Result<()> {
let base_dir = normalize_path_for_lua(&layout.base_dir);
register_mirror_dir(lua, &base_dir)?;
register_get_env(lua)?;
setup_require(lua, &base_dir)?;
Ok(())
}
/// 注入全局锚点变量
pub fn register_mirror_dir(lua: &Lua, base_dir: &str) -> Result<()> {
debug!("设置 {MIRROR_DIR} 环境变量");
lua.globals().set(MIRROR_DIR, base_dir)?;
// .context(format!("设置 {MIRROR_DIR} 环境变量失败"))?;
Ok(())
}
///注入 get_env 供配置读取环境变量
pub fn register_get_env(lua: &Lua) -> Result<()> {
let globals = lua.globals();
let get_env = lua.create_function(|lua, key: String| -> mlua::Result<Table> {
// 缺失变量视为空字符串,拆分后得到空表(不贡献任何路径段)
let value = env::var_os(key).unwrap_or_default();
// 空输入返回空表;否则按平台分隔符拆分(split_paths 会剥离引号包裹)
if value.is_empty() {
return lua.create_table();
}
let table = lua.create_table()?;
for (i, p) in env::split_paths(&value).enumerate() {
// 3. 跨平台提取原始字节并转为 LuaString,保证 100% 无损
#[cfg(unix)]
let lua_str = {
use std::os::unix::ffi::OsStrExt;
lua.create_string(p.as_os_str().as_bytes())?
};
#[cfg(windows)]
let lua_str = {
// Windows 路径是 UTF-16,转成字符串或保持其字节表达
let s = p.to_string_lossy();
lua.create_string(s.as_bytes())?
};
table.set(i + 1, lua_str)?;
}
Ok(table)
})?;
// .context("注册 get_env 函数失败")?;
globals.set("get_env", get_env)?;
// .context("挂载 get_env 全局函数失败")?;
Ok(())
}
/// 配置 package.path 并包装 require,拦截加载失败以提高容错性
pub fn setup_require(lua: &Lua, base_dir: &str) -> Result<()> {
let globals = lua.globals();
// 1. 安全加固并拓展 package 搜索路径
let package = globals.get::<Table>("package")?;
let _ = package.set("cpath", "");
let _ = package.set("loadlib", Value::Nil);
let commands_dir = format!("{}/commands", base_dir);
let vendor_dir = format!("{}/vendor", base_dir);
let path = package.get::<String>("path")?;
let new_path = format!(
"{};{}/?.lua;{}/?/init.lua;{}/?.lua;{}/?/init.lua;{}/?.lua;{}/?/init.lua;",
path, base_dir, base_dir, commands_dir, commands_dir, vendor_dir, vendor_dir
);
debug!("重新写入的 package 查找路径");
package.set("path", new_path)?;
// 2. 获取原生 require 并通过闭包直接持有(无需向全局表注入备份变量)
let original_require: mlua::Function = globals.get("require")?;
// .context("获取内置 require 函数失败")?;
let wrapped_require =
lua.create_function(move |_lua, module: String| -> mlua::Result<Value> {
match original_require.call::<Value>(module.as_str()) {
Ok(value) => Ok(value),
Err(e) => {
warn!(
module = %module,
error = %e,
"配置模块加载失败,已跳过该条目(可在独立配置文件中定义)"
);
Ok(Value::Nil)
}
}
})?;
// .context("创建包装版 require 函数失败")?;
// 3. 覆盖全局 require
globals.set("require", wrapped_require)?;
// .context("重载 require 函数失败")?;
Ok(())
}