Files
mirror/src/runtime.rs
CNWei ed5439eaa1 refactor: 拆分代码结构为 lib.rs 与多模块架构
- 将核心业务逻辑抽离至 lib.rs,精简 main.rs 为纯粹的 CLI 入口
- 将代码按职责拆分为 config、env、runtime、shim 等子模块
- 提高可测试性与代码复用度
2026-08-14 10:36:09 +08:00

87 lines
3.1 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<Self, ShimError> {
// 只加载安全的标准库,剥离 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<String> {
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::<Table>("package") {
if let Ok(path) = package.get::<String>("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<Table, ShimError> {
let code = fs::read_to_string(path)?;
self.lua
.load(&code)
.set_name(path.to_string_lossy())
.eval::<Table>()
.map_err(|e| ShimError::LuaExecutionError {
file: path.display().to_string(),
source: e,
})
}
/// 将 Lua Value 解析转化为 ShimConfig 数据对象
pub fn parse_config(&self, value: Value) -> Result<ShimConfig, ShimError> {
ShimConfig::from_lua(value, &self.lua).map_err(|e| ShimError::InvalidConfig(e.to_string()))
}
}