fix(config): 优化 layout

- 新增 从环境变量解析目录结构
This commit is contained in:
2026-08-20 14:30:14 +08:00
parent f59040c11e
commit 428da76a81
8 changed files with 184 additions and 471 deletions

331
Cargo.lock generated
View File

@@ -1,331 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "bitflags"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]]
name = "bstr"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f"
dependencies = [
"memchr",
"serde_core",
]
[[package]]
name = "cc"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "either"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
[[package]]
name = "find-msvc-tools"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "fs-err"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91aa448ca50d7e79433bdf3ee8d99215430d2ec02ade5aefab2a073a1822e8a"
dependencies = [
"autocfg",
]
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "lua-src"
version = "550.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75c110c2fa33f34e0de05448e1f3eb2e0631e7a69e2d8ae1586cffc9fc9f9949"
dependencies = [
"cc",
]
[[package]]
name = "luajit-src"
version = "210.7.2+b925b3e"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "920cf654b23d217c550ceea57c32cd2a413ea27b6d47ed77b5ee0cf655adefa6"
dependencies = [
"cc",
"which",
]
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "mlua"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad72ffa037cf5970c9860674f32f703fda25d86cf217475fe7a79c5f9961bcaa"
dependencies = [
"bstr",
"either",
"libc",
"mlua-sys",
"num-traits",
"parking_lot",
"rustc-hash",
]
[[package]]
name = "mlua-sys"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92136787b906d4e55cfe96cd6c62e010bb1a56889d0d6cf83eb016dbad07576b"
dependencies = [
"cc",
"cfg-if",
"libc",
"lua-src",
"luajit-src",
"pkg-config",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-link",
]
[[package]]
name = "pkg-config"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
]
[[package]]
name = "rshim"
version = "0.1.0"
dependencies = [
"fs-err",
"mlua",
"thiserror",
"unicode-bom",
"winapi",
]
[[package]]
name = "rustc-hash"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "smallvec"
version = "1.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
[[package]]
name = "syn"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-bom"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "which"
version = "8.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3ef584124b911bcc3875c2f1472e80f24361ceb789bd1c62b3e9a3df9ff43c"
dependencies = [
"libc",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"

View File

@@ -34,13 +34,13 @@ fn main() {
}
// 复制文件
let source = PathBuf::from(&manifest_dir).join("shims.lua");
let source = PathBuf::from(&manifest_dir).join("mimic.lua");
let destination = output_dir.join("rshim.lua");
if source.exists() {
fs::copy(&source, &destination).expect("Failed to copy shims.lua");
println!("Copied shims.lua to: {:?}", destination);
fs::copy(&source, &destination).expect("Failed to copy mimic.lua");
println!("Copied mimic.lua to: {:?}", destination);
} else {
panic!("shims.lua not found at: {:?}", source);
panic!("mimic.lua not found at: {:?}", source);
}
}

View File

@@ -1,4 +1,4 @@
-- shims.lua (总控制台)
-- mimic.lua (总控制台)
-- __SHIM_DIR__ 已经在 Rust 中注入完毕,并且是正斜杠格式 (例如 C:/Tools)
local base_dir = __SHIM_DIR__

View File

@@ -1,4 +1,4 @@
use mlua::{FromLua, Lua, LuaString, Table, Value};
use mlua::{FromLua, Lua, LuaString, ObjectLike, Table, Value};
use std::collections::HashMap;
use std::ffi::OsString;
use std::fmt;
@@ -35,7 +35,7 @@ fn lua_string_2_os_string(s: LuaString) -> mlua::Result<OsString> {
let (cow, _, had_errors) = encoding_rs::GBK.decode(raw_bytes);
if had_errors {
return Err(mlua::Error::RuntimeError(
"Path contains invalid/unsupported byte encoding".into(),
"路径包含无效/不支持的字节编码".into(),
));
}
Ok(OsString::from(cow.as_ref()))
@@ -43,7 +43,31 @@ fn lua_string_2_os_string(s: LuaString) -> mlua::Result<OsString> {
}
/// 按连续整数下标遍历表的序列部分;存在空洞或非序列键时返回错误,避免静默截断
/// 值分流与上下文分发枚举
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValueShunt<'a> {
/// 命令行参数元素:仅支持一维基础标量,禁止嵌套 Table
Args,
/// 环境变量值:支持基础标量及多维嵌套 Table递归展平
Env(&'a str),
}
impl<'a> Display for ValueShunt<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Args => write!(f, "命令行参数 (args)"),
Self::Env(name) => write!(f, "环境变量 ({name})"),
}
}
}
impl<'a> ValueShunt<'a> {
fn parse_sequence(&self, tbl: &Table, out: &mut Vec<OsString>) -> mlua::Result<()> {
self.validate_each_sequence_item(tbl, |item| self.collect_into(item, out))
}
fn validate_each_sequence_item(
&self,
tbl: &Table,
mut f: impl FnMut(Value) -> mlua::Result<()>,
) -> mlua::Result<()> {
@@ -61,40 +85,29 @@ fn validate_each_sequence_item(
let (key, _) = pair?;
match key {
Value::Integer(i) if i >= 1 && i < index => {}
_ => {
Value::Integer(_) => {
return Err(conversion_error(format!(
"数组只能包含连续的整数下标 [1..{}],发现非序列键或空洞",
index - 1
"{}{} 个元素为 nil 或不存在(请检查是否漏写引号或变量未定义)",
self, index
)));
}
_ => {
// let label = match self {
// ValueShunt::Args => "命令行参数 (args)".to_string(),
// ValueShunt::Env(n) => format!("环境变量 [{n}]"),
// };
return Err(conversion_error(format!(
"{} 必须是纯列表,不能包含键值对/字典结构",
self
)));
}
}
}
Ok(())
}
/// 值分流与上下文分发枚举
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValueShunt {
/// 命令行参数元素:仅支持一维基础标量,禁止嵌套 Table
Args,
/// 环境变量值:支持基础标量及多维嵌套 Table递归展平
Env,
}
impl Display for ValueShunt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Args => write!(f, "命令行参数 (args)"),
Self::Env => write!(f, "环境变量 (env)"),
}
}
}
impl ValueShunt {
fn expected_types_desc(&self) -> &str {
match self {
Self::Args => "string/number/boolean不支持嵌套数组",
Self::Env => "string/number/boolean 或包含这些类型的数组/嵌套数组",
}
}
impl<'a> ValueShunt<'a> {
/// 递归/标量收集实现
pub fn collect_into(&self, value: Value, out: &mut Vec<OsString>) -> mlua::Result<()> {
match value {
@@ -110,15 +123,32 @@ impl ValueShunt {
out.push(OsString::from(b.to_string()));
}
// 嵌套 Table 仅在 EnvValue 下允许递归展开(支持 get_env("PATH") 等返回的子表)
Value::Table(tbl) if *self == Self::Env => {
validate_each_sequence_item(&tbl, |item| self.collect_into(item, out))?;
// Value::Table(tbl) if *self == Self::Env(_) => {
// self.validate_each_sequence_item(&tbl, |item| self.collect_into(item, out))?;
// }
Value::Table(tbl) => {
match self {
Self::Args => {
return Err(conversion_error(format!(
"{} 不支持 {} 类型, 仅支持 string/number/boolean",
self,
tbl.to_string()?
)));
}
Self::Env(_) => {
self.validate_each_sequence_item(&tbl, |item| {
self.collect_into(item, out)
})?;
}
}
// self.validate_each_sequence_item(&tbl, |item| self.collect_into(item, out))?;
}
other => {
return Err(conversion_error(format!(
"{} 不支持类型 {}(仅支持 {}",
"{} 不支持 {} 类型, 仅支持 string/number/boolean 或包含这些类型的数组/嵌套数组",
self,
other.type_name(),
self.expected_types_desc()
)));
}
}
@@ -127,7 +157,19 @@ impl ValueShunt {
}
/// 校验环境变量名的合法性Windows 约束:非空、不含 '='、不含 NUL
fn validate_env_var_name(key: &str) -> mlua::Result<()> {
fn validate_env_var_name(key_val: &Value, val: &Value) -> mlua::Result<String> {
let key = match key_val {
Value::String(s) => s.to_str()?.to_string(),
_ => {
return Err(conversion_error(format!(
r#"env 配置解析失败:值: {} (类型: {}) 无效的键值!
示例 HOME="/home" 或 PATH = {{ "C:/tools/", "D:/Windows"}}"#,
val.to_string()?,
val.type_name()
)));
}
};
if key.is_empty() {
return Err(conversion_error("环境变量名不能为空"));
}
@@ -143,42 +185,11 @@ fn validate_env_var_name(key: &str) -> mlua::Result<()> {
key
)));
}
Ok(())
Ok(key)
}
/// 递归将任意 Lua Value 展开为扁平的字符串片段列表
fn collect_env_segments(value: Value, out: &mut Vec<OsString>) -> mlua::Result<()> {
match value {
// 1. 字符串
Value::String(s) => out.push(lua_string_2_os_string(s)?),
// 2. 整数与浮点数
Value::Integer(i) => out.push(OsString::from(i.to_string())),
Value::Number(n) => {
tracing::warn!(value = %n, "环境变量中的浮点数将按十进制格式转换为字符串");
out.push(OsString::from(n.to_string()));
}
// 3. 布尔值
Value::Boolean(b) => {
tracing::warn!(value = %b, "环境变量中的布尔值将转换为字符串");
out.push(OsString::from(b.to_string()));
}
// 4. 表/数组:必须是连续整数下标的纯序列,递归解包(支持任意深度的嵌套数组)
Value::Table(tbl) => {
validate_each_sequence_item(&tbl, |item| collect_env_segments(item, out))?
}
// 5. 安全忽
// 略 nil
Value::Nil => {}
// 6. 无法转为环境变量的非法类型函数、协程、UserData 等):报错而非静默忽略
other => {
return Err(conversion_error(format!(
"环境变量值不支持类型 {}(仅支持 string/number/boolean/数组)",
other.type_name()
)));
}
}
Ok(())
}
// fn collect_env_segments(value: Value, out: &mut Vec<OsString>) -> mlua::Result<()>{OK(())}
#[derive(Debug, Clone, Default)]
pub struct ShimConfig {
@@ -243,11 +254,7 @@ impl FromLua for ShimConfig {
// 可选字段: args缺失或 nil 时默认空列表;保留空字符串参数,与提权路径的 "" 语义一致)
match table.get::<Option<Value>>("args")? {
None | Some(Value::Nil) => {}
Some(Value::Table(tbl)) => {
validate_each_sequence_item(&tbl, |item| {
ValueShunt::Args.collect_into(item, &mut args)
})?;
}
Some(Value::Table(tbl)) => ValueShunt::Args.parse_sequence(&tbl, &mut args)?,
Some(other) => {
return Err(conversion_error(format!(
"args 必须是数组列表,实际类型是 {}",
@@ -262,12 +269,13 @@ impl FromLua for ShimConfig {
match env_table {
None | Some(Value::Nil) => {}
Some(Value::Table(tbl)) => {
for pair in tbl.pairs::<String, Value>() {
for pair in tbl.pairs::<Value, Value>() {
let (name, value) = pair?;
validate_env_var_name(&name)?;
let name = validate_env_var_name(&name, &value)?;
println!("==========={name}:{:?}", value);
let mut parts = Vec::new();
ValueShunt::Env.collect_into(value, &mut parts)?;
ValueShunt::Env(&name).collect_into(value, &mut parts)?;
// 空数组/空串也显式设置(空值表示清空该变量),
// 与“未配置该变量(继承宿主环境)”相区分
@@ -277,7 +285,7 @@ impl FromLua for ShimConfig {
name, e
))
})?;
println!("环境变量拼接结果:{:?}", joined_os_str);
// println!("环境变量拼接结果:{:?}", joined_os_str);
// 检查是否包含非法 NUL 字符
#[cfg(unix)]
{
@@ -358,11 +366,7 @@ mod tests {
PATH = { "C:/tools/git/bin", "C:/Windows", get_env("PATH") },
HOME = "C:/tools/home",
CONST = 3,
BOOL = true
MUT ={
A=3
B=true
}
BOOL = true
}
}
"#,
@@ -470,7 +474,9 @@ mod tests {
#[test]
fn rejects_non_string_args_element() {
assert!(parse(r#"return { target = "t.exe", args = { 1 } }"#).is_err());
// ValueShunt::Args 允许基础标量(数字/布尔)转字符串,仅禁止嵌套表
let cfg = parse(r#"return { target = "t.exe", args = { 1, true } }"#).unwrap();
assert_eq!(cfg.args, vec!["1", "true"]);
}
#[test]

View File

@@ -1,36 +1,67 @@
use anyhow::{Context, Result, bail};
use std::env;
use std::path::{Path, PathBuf};
pub struct ShimLayout {
pub root_dir: PathBuf,
use tracing::debug;
pub struct Layout {
pub base_dir: PathBuf,
pub bin_dir: PathBuf,
pub tools_dir: PathBuf,
pub lua_file: PathBuf,
}
impl Layout {
/// 自动解析目录布局:
/// 1. 优先使用环境变量 RSHIM_HOME
/// 2. 兜底回退到当前垫片可执行文件所在目录推断 (exe -> bin -> root)
pub fn discover(current_exe: &Path) -> Result<Self> {
// 策略 1: 环境变量优先
if let Ok(home_val) = env::var("MIMIC_HOME") {
let trimmed = home_val.trim();
if !trimmed.is_empty() {
debug!(home = %trimmed, "检测到 MIMIC_HOME采用环境变量配置");
return Self::from_base_dir(PathBuf::from(trimmed));
}
}
// 策略 2: 相对路径自动推断兜底
debug!("未配置 MIMIC_HOME尝试从当前可执行文件路径推断根目录");
Self::from_executable(current_exe)
}
/// 基于确定的根目录构建完整布局
fn from_base_dir(base_dir: PathBuf) -> Result<Self> {
if !base_dir.is_dir() {
bail!("指定的根目录不存在或不是有效目录: [{}]", base_dir.display());
}
let bin_dir = base_dir.join("bin");
let tools_dir = base_dir.join("tools");
let lua_file = base_dir.join("mimic.lua");
Ok(Self {
base_dir,
bin_dir,
tools_dir,
lua_file,
})
}
impl ShimLayout {
/// 从当前可执行文件解析 shim 安装目录布局
pub fn from_executable(exe_path: impl AsRef<Path>) -> Result<Self> {
let exe_path = exe_path.as_ref();
fn from_executable(exe_path: &Path) -> Result<Self> {
// let exe_path = exe_path.as_ref();
let bin_dir = exe_path
.parent()
.with_context(|| format!("无法获取程序 [{}] 的父级 bin 目录", exe_path.display()))?
.to_path_buf();
eprintln!("bin_dir目录 {}", bin_dir.display());
debug!("bin_dir目录 {}", bin_dir.display());
let root_dir = bin_dir
let base_dir = bin_dir
.parent()
.with_context(|| format!("无法获取 bin 目录 [{}] 的父级 root 目录", bin_dir.display()))?
.to_path_buf();
eprintln!("root_dir 目录 {}", root_dir.display());
debug!("base_dir 目录 {}", base_dir.display());
let tools_dir = root_dir.join("tools");
eprintln!("tools_dir 目录 {}", tools_dir.display());
Ok(Self {
root_dir,
bin_dir,
tools_dir,
})
Self::from_base_dir(base_dir)
}
}

View File

@@ -3,8 +3,9 @@ mod layout;
mod logger;
mod runtime;
mod shim;
mod loader;
pub use config::ShimConfig;
pub use layout::ShimLayout;
pub use layout::Layout;
pub use runtime::LuaRuntime;
pub use shim::Shim;

View File

@@ -1,4 +1,4 @@
use crate::{ShimConfig, ShimLayout};
use crate::{ShimConfig, Layout};
use anyhow::{Context, Result, anyhow, bail};
use mlua::{FromLua, Lua, StdLib, Table, Value};
use std::ffi::OsStr;
@@ -17,7 +17,7 @@ pub struct LuaRuntime {
impl LuaRuntime {
/// 初始化限定权限的 Lua 沙箱环境
pub fn new(layout: &ShimLayout) -> Result<Self> {
pub fn new(layout: &Layout) -> Result<Self> {
// 只加载安全的标准库,剥离 os / io 等风险模块
let lua = Lua::new_with(
StdLib::TABLE | StdLib::STRING | StdLib::MATH | StdLib::PACKAGE,
@@ -28,12 +28,12 @@ impl LuaRuntime {
let globals = lua.globals();
// 统一使用 POSIX 风格路径规范化路径字符串
let root_dir = normalize_path_for_lua(&layout.root_dir);
let base_dir = normalize_path_for_lua(&layout.base_dir);
let tools_dir = normalize_path_for_lua(&layout.tools_dir);
// 1. 注入锚点变量 __SHIM_DIR__shim 安装根目录)
globals
.set("__SHIM_DIR__", root_dir.clone())
.set("__SHIM_DIR__", base_dir.clone())
.context("设置 __SHIM_DIR__ 环境变量失败")?;
// 2. 安全暴露 get_env 供配置读取环境变量
@@ -82,14 +82,14 @@ impl LuaRuntime {
if let Ok(path) = package.get::<String>("path") {
let new_path = format!(
"{};{}/?.lua;{}/?/init.lua;{}/?.lua;{}/?/init.lua",
path, root_dir, root_dir, tools_dir, tools_dir
path, base_dir, base_dir, tools_dir, tools_dir
);
let _ = package.set("path", new_path);
}
}
// 4. 包装 require配置模块缺失/加载失败时记录日志并跳过该条目,
// 而不是让整个 shims.lua 解析失败(排查问题时日志可见)
// 而不是让整个 mimic.lua 解析失败(排查问题时日志可见)
let original_require: mlua::Function = globals
.get("require")
.context("获取内置 require 函数失败")?;
@@ -145,23 +145,20 @@ impl LuaRuntime {
.with_context(|| format!("执行 Lua 配置文件失败: {}", path.display()))
}
// /// 将 Lua Value 解析转化为 ShimConfig 数据对象
// pub fn parse_config(&self, value: Value) -> Result<ShimConfig, Error> {
// ShimConfig::from_lua(value, &self.lua).map_err(|e| Error::InvalidConfig(e.to_string()))
// }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ShimLayout;
use crate::Layout;
fn test_layout() -> ShimLayout {
fn test_layout() -> Layout {
let root = std::env::temp_dir().join("rshim-test-layout");
ShimLayout {
root_dir: root.clone(),
Layout {
base_dir: root.clone(),
bin_dir: root.join("bin"),
tools_dir: root.join("tools"),
lua_file: Default::default(),
}
}

View File

@@ -1,4 +1,4 @@
use crate::{LuaRuntime, ShimConfig, ShimLayout};
use crate::{LuaRuntime, ShimConfig, Layout};
use anyhow::{Context, Result, bail};
use mlua::{Table, Value};
use std::env;
@@ -20,10 +20,10 @@ impl Shim {
current_exe = %current_exe.display(),
"开始加载 Shim 配置"
);
let layout = Layout::discover(&current_exe)?;
let layout = ShimLayout::from_executable(current_exe)?;
debug!(
root_dir = %layout.root_dir.display(),
root_dir = %layout.base_dir.display(),
bin_dir = %layout.bin_dir.display(),
tools_dir = %layout.tools_dir.display(),
"Shim 目录布局解析完成"
@@ -36,16 +36,25 @@ impl Shim {
fn resolve_config(
runtime: &LuaRuntime,
paths: &ShimLayout,
paths: &Layout,
target_name: &str,
) -> Result<ShimConfig> {
// 策略 1: 尝试加载全局配置文件 shims.lua
let global_config = paths.root_dir.join("rshim.lua");
// 策略 1: 尝试加载全局配置文件 mimic.lua
let global_config = paths.base_dir.join("rshim.lua");
// if !global_config.is_file() {
// bail!(
// "未找到主配置文件: [{}],请确保在安装根目录创建 rshim.lua",
// global_config.display()
// );
// }
if global_config.is_file() {
trace!(path = %global_config.display(), "发现全局配置文件,尝试解析");
let root_table: Table = runtime.eval_script(&global_config)?;
// 检查 shims.lua 中是否存在以 target_name 命名的 Table 节点
// 检查 mimic.lua 中是否存在以 target_name 命名的 Table 节点
if root_table
.contains_key(target_name)
.context("检查全局配置失败")?
@@ -69,7 +78,7 @@ impl Shim {
let target_filename = format!("{}.lua", target_name);
let candidates = [
paths.tools_dir.join(&target_filename),
paths.root_dir.join(&target_filename),
paths.base_dir.join(&target_filename),
];
for config_path in &candidates {
@@ -84,7 +93,7 @@ impl Shim {
// 策略 3: 所有查找失败,抛出错误
warn!(target = %target_name, "未找到任何匹配的配置文件");
bail!(
"未找到关于 '{}' 的配置。请检查 shims.lua 或特定的 {}.lua 文件",
"未找到关于 '{}' 的配置。请检查 mimic.lua 或特定的 {}.lua 文件",
target_name,
target_name
);