use crate::error::ShimError; use crate::{ShimConfig, ShimEnv}; use mlua::{FromLua, Lua, StdLib, Table, Value}; use std::path::Path; use std::{env, fs}; /// 将 Path 转换为适合 Lua 使用的安全字符串路径 fn normalize_path_for_lua(path: &Path) -> String { let path_str = path.to_string_lossy(); // 1. 剥离 Windows UNC 规范路径前缀 (\\?\) let clean_str = path_str.strip_prefix(r"\\?\").unwrap_or(&path_str); // 2. 将反斜杠转换成正斜杠(在非 UNC 路径下,Windows 和 Lua 均完美支持 /) // 这样既避免了 Lua 字符串转义隐患,又不会破坏 Windows 路径 clean_str.replace('\\', "/") } pub struct LuaRuntime { lua: Lua, } impl LuaRuntime { /// 初始化限定权限的 Lua 沙箱环境 pub fn new(shim_env: &ShimEnv) -> Result { // 只加载安全的标准库,剥离 os / io 等风险模块 let lua = Lua::new_with( StdLib::TABLE | StdLib::STRING | StdLib::MATH | StdLib::PACKAGE, mlua::LuaOptions::default(), ) .map_err(|e| ShimError::EnvError(format!("初始化 Lua 失败: {}", e)))?; let globals = lua.globals(); // 统一使用 POSIX 风格路径规范化路径字符串 let root_dir_str = normalize_path_for_lua(&shim_env.root_dir); let tools_dir_str = normalize_path_for_lua(&shim_env.tools_dir); // 1. 注入锚点变量 globals .set("__SHIM_DIR__", root_dir_str.clone()) .map_err(|e| ShimError::EnvError(e.to_string()))?; // 2. 安全暴露 get_env 供配置读取环境变量 let get_env = lua .create_function(|_, key: String| -> mlua::Result { Ok(env::var(key).unwrap_or_default()) }) .map_err(|e| ShimError::EnvError(e.to_string()))?; globals .set("get_env", get_env) .map_err(|e| ShimError::EnvError(e.to_string()))?; // 3. 配置 package.path,确保 require 行为正常 if let Ok(package) = globals.get::("package") { if let Ok(path) = package.get::("path") { let new_path = format!( "{};{}/?.lua;{}/?/init.lua;{}/?.lua;{}/?/init.lua", path, root_dir_str, root_dir_str, tools_dir_str, tools_dir_str ); let _ = package.set("path", new_path); } } Ok(Self { lua }) } /// 执行指定脚本文件,直接返回完整的 Lua Table pub fn evaluate_lua_script(&self, path: &Path) -> Result { let code = fs::read_to_string(path)?; self.lua .load(&code) .set_name(path.to_string_lossy()) .eval::
() .map_err(|e| ShimError::LuaExecutionError { file: path.display().to_string(), source: e, }) } /// 将 Lua Value 解析转化为 ShimConfig 数据对象 pub fn parse_config(&self, value: Value) -> Result { ShimConfig::from_lua(value, &self.lua).map_err(|e| ShimError::InvalidConfig(e.to_string())) } }