refactor: 拆分代码结构为 lib.rs 与多模块架构

- 将核心业务逻辑抽离至 lib.rs,精简 main.rs 为纯粹的 CLI 入口
- 将代码按职责拆分为 config、env、runtime、shim 等子模块
- 提高可测试性与代码复用度
This commit is contained in:
2026-08-14 10:36:09 +08:00
parent e52ba67d7e
commit ed5439eaa1
8 changed files with 305 additions and 288 deletions

86
src/runtime.rs Normal file
View File

@@ -0,0 +1,86 @@
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()))
}
}