From 4aa619f07ed51f854b5264a4cc84628cdc40725e Mon Sep 17 00:00:00 2001 From: CNWei Date: Wed, 12 Aug 2026 20:19:50 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=BC=95=E6=93=8E=E8=BF=81=E7=A7=BB=E8=87=B3=20mlua?= =?UTF-8?q?=20=E5=B9=B6=E4=BC=98=E5=8C=96=E7=8E=AF=E5=A2=83=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 配置文件解析引擎由 shim 替换为 mlua (Lua 5.4) - 优化路由策略:统一以根目录 shims.lua 为入口,支持 tools/ 目录模块化降级探测 - 自动注入 __SHIM_DIR__ 绝对路径与 package.path 模块搜索路径 - 环境变量改用 Command::envs 安全注入,并在 UAC 提权时透传 --- .gitignore | 3 +- Cargo.lock | 276 ++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 16 +-- shims.lua | 44 ++++++++ src/main.rs | 57 +++++++---- src/shims.rs | 225 +++++++++++++++++++++++++++-------------- 6 files changed, 516 insertions(+), 105 deletions(-) create mode 100644 shims.lua diff --git a/.gitignore b/.gitignore index 153e995..bd74b9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /.vscode -*.exe \ No newline at end of file +*.exe +./Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index e3198ec..281cec3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,25 +1,285 @@ # 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 = "2.5.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcd1163ae48bda72a20ae26d66a04d3094135cadab911cff418ae5e33f253431" +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", "unicode-bom", "winapi", ] [[package]] -name = "unicode-bom" -version = "1.1.4" +name = "rustc-hash" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63ec69f541d875b783ca40184d655f2927c95f0bffd486faa83cd3ac3529ec32" +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 = "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" @@ -42,3 +302,9 @@ 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" diff --git a/Cargo.toml b/Cargo.toml index 393ac81..f218510 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "rshim" version = "0.1.0" authors = ["anonymous "] -edition = "2018" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -11,12 +11,10 @@ edition = "2018" opt-level = "z" panic = "abort" [dependencies] -fs-err = "2.5.0" -unicode-bom = "1" - -[dependencies.winapi] -version = "0.3" -features = [ +fs-err = "3.3.1" +unicode-bom = "2.0.3" +mlua = { version = "0.12.0", features = ["lua54", "vendored"] } +winapi = { version = "0.3", features = [ "wincon", "consoleapi", "minwindef", @@ -28,4 +26,6 @@ features = [ "processthreadsapi", "objbase", "impl-default" -] +] } + + diff --git a/shims.lua b/shims.lua new file mode 100644 index 0000000..0851c16 --- /dev/null +++ b/shims.lua @@ -0,0 +1,44 @@ +-- shims.lua (总控制台) +-- __SHIM_DIR__ 已经在 Rust 中注入完毕,并且是正斜杠格式 (例如 C:/Tools) +local base_dir = __SHIM_DIR__ + +return { + --------------------------------------------------- + -- 1. 标准相对路径 + 正斜杠拼接 (最推荐,绿色便携) + --------------------------------------------------- + ["git"] = { + path = base_dir .. "/git/bin/git.exe", + -- 追加参数 + args = {"--no-pager"}, + -- 注入环境变量,使用 get_env 获取宿主机当前值 + env = { + PATH = base_dir .. "/git/bin;" .. get_env("PATH") + } + }, + + --------------------------------------------------- + -- 2. Lua 原生字符串语法 [[ ]] (适合直接从 Windows 复制绝对路径) + --------------------------------------------------- + ["python"] = { + -- 在 [[]] 内部,\ 不需要写成 \\,直接粘贴即可 + path = [[C:\Python310\python.exe]], + args = {"-B"}, + -- 空字典也是合法的,等同于不设置 + env = {} + }, + + --------------------------------------------------- + -- 3. 极简参数覆盖 (没有 args 和 env) + --------------------------------------------------- + ["curl"] = { + path = base_dir .. "/curl/curl.exe" + }, + + --------------------------------------------------- + -- 4. 模块化路由 (得益于 Rust 注入的 package.path) + -- require 能够直接在当前目录 或 conf.d/ 目录下寻找 node.lua + --------------------------------------------------- + ["node"] = require("node"), + + ["npm"] = require("npm") +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index fc22cfa..ddd6d1f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,9 +3,11 @@ use std::{ ffi::CString, mem::size_of, path::Path, - process::{exit, Command}, + process::{Command, exit}, ptr::null_mut, }; +mod shims; +use shims::Shim; use winapi::{ shared::minwindef::{BOOL, DWORD, FALSE, TRUE}, @@ -14,7 +16,7 @@ use winapi::{ consoleapi, objbase::{COINIT_APARTMENTTHREADED, COINIT_DISABLE_OLE1DDE}, processthreadsapi::GetExitCodeProcess, - shellapi::{ShellExecuteExA, SEE_MASK_NOASYNC, SEE_MASK_NOCLOSEPROCESS, SHELLEXECUTEINFOA}, + shellapi::{SEE_MASK_NOASYNC, SEE_MASK_NOCLOSEPROCESS, SHELLEXECUTEINFOA, ShellExecuteExA}, synchapi::WaitForSingleObject, winbase::INFINITE, wincon, @@ -30,15 +32,12 @@ unsafe extern "system" fn routine_handler(evt: DWORD) -> BOOL { wincon::CTRL_LOGOFF_EVENT => TRUE, //eprintln!("ctrl_logoff handled!"), wincon::CTRL_SHUTDOWN_EVENT => TRUE, //eprintln!("ctrl_shutdown handled!"), other => { - eprintln!("unknown event number: {}, unhandled!", other); - return FALSE; + eprintln!("未知的系统事件编号: {},未处理!", other); + FALSE } } } -mod shims; -use shims::Shim; - const EXIT_FAILED_LOAD_SHIM: i32 = 1; const EXIT_FAILED_SPAWN_PROG: i32 = 2; const EXIT_FAILED_WAIT_PROG: i32 = 3; @@ -48,31 +47,42 @@ const ERROR_ELEVATION_REQUIRED: i32 = 740; fn main() { let res: BOOL = unsafe { consoleapi::SetConsoleCtrlHandler(Some(routine_handler), TRUE) }; if res == FALSE { - eprintln!("shim: register Ctrl handler failed."); + eprintln!("警告: 注册控制台中断事件处理器失败。"); } let calling_args: Vec<_> = env::args().skip(1).collect(); let shim = match Shim::init() { Ok(v) => v, Err(e) => { - eprintln!("Error while loading shim: {}", e); + eprintln!("加载代理(shim)配置时发生错误: {}", e); exit(EXIT_FAILED_LOAD_SHIM); } }; + let args = if let Some(mut shim_args) = shim.args { shim_args.extend_from_slice(calling_args.as_slice()); shim_args } else { calling_args }; - let mut cmd = match Command::new(&shim.target_path).args(&args).spawn() { + // ======= 【修改位置 1:构建 Command 并注入环境变量】 ======= + let mut cmd_builder = Command::new(&shim.target_path); + cmd_builder.args(&args); + + // 仅作用于目标子进程,完全 Safe 且隔离 + if let Some(ref envs) = shim.envs { + cmd_builder.envs(envs); + } + let mut cmd = match cmd_builder.spawn() { Ok(v) => v, - Err(e) if e.raw_os_error() == Some(ERROR_ELEVATION_REQUIRED) => { - exit(execute_elevated(&shim.target_path, &args)) - } + Err(e) if e.raw_os_error() == Some(ERROR_ELEVATION_REQUIRED) => exit(execute_elevated( + &shim.target_path, + &args, + shim.envs.as_ref(), + )), Err(e) => { eprintln!( - "Error while spawning target program `{}`: {}", + "启动目标程序 [{}] 时发生错误: {}", shim.target_path.to_string_lossy(), e ); @@ -83,7 +93,7 @@ fn main() { Ok(v) => v, Err(e) => { eprintln!( - "Error while waiting target program `{}`: {}", + "等待目标程序 [{}] 执行完毕时发生错误: {}", shim.target_path.to_string_lossy(), e ); @@ -93,7 +103,20 @@ fn main() { exit(status.code().unwrap_or(EXIT_PROG_TERMINATED)) } -fn execute_elevated(program: &Path, args: &[String]) -> i32 { +fn execute_elevated( + program: &Path, + args: &[String], + envs: Option<&std::collections::HashMap>, +) -> i32 { + // 若提权启动,在此处将环境变量设置给当前进程(即将弹窗 UAC 的进程,随后会被子进程继承) + if let Some(env_map) = envs { + for (k, v) in env_map { + unsafe { + env::set_var(k, v); + } + } + } + let runas = CString::new("runas").unwrap(); let program = CString::new(program.to_str().unwrap()).unwrap(); let mut params = String::new(); @@ -141,5 +164,5 @@ fn execute_elevated(program: &Path, args: &[String]) -> i32 { return EXIT_FAILED_WAIT_PROG; } } - return code as i32; + code as i32 } diff --git a/src/shims.rs b/src/shims.rs index 45dd318..e2201d5 100644 --- a/src/shims.rs +++ b/src/shims.rs @@ -1,89 +1,166 @@ -use fs_err as fs; +use mlua::{Error as LuaError, Lua, Table, Value}; use std::{ collections::HashMap, - env, + env, fs, io::{Error, ErrorKind}, path::{Path, PathBuf}, }; + pub struct Shim { pub target_path: PathBuf, pub args: Option>, + pub envs: Option>, } impl Shim { pub fn init() -> Result { - let shim_path = get_shim_file_path()?; - let kvs = parse_shim_file(&shim_path)?; - let target_path = match kvs.get("path") { - Some(p) => PathBuf::from(p), - None => { - return Err(Error::new( - ErrorKind::NotFound, - format!("no path key in {}", shim_path.to_string_lossy()), + let (_exe_path, _exe_dir, root_dir, tools_dir, exe_name) = Self::get_exe_context()?; + + let lua = Lua::new(); + let globals = lua.globals(); + + // 1. 将根目录 (Root) 注入为 __SHIM_DIR__,作为全局相对路径的锚点 + let root_dir_str = root_dir.to_string_lossy().replace('\\', "/"); + let tools_dir_str = tools_dir.to_string_lossy().replace('\\', "/"); + globals + .set("__SHIM_DIR__", root_dir_str.clone()) + .map_err(|e| Error::new(ErrorKind::Other, format!("注入 __SHIM_DIR__ 失败: {}", e)))?; + + // 2. 注册 get_env 获取环境变量的函数 + let get_env = lua + .create_function(|_, key: String| -> Result { + Ok(env::var(key).unwrap_or_default()) + }) + .map_err(|e| Error::new(ErrorKind::Other, format!("注册 get_env 失败: {}", e)))?; + + globals + .set("get_env", get_env) + .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; + + // 3. 增强 package.path,让 require 自动在当前目录及 conf.d 目录下寻找模块 + 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); + } + } + + Self::resolve_config(&lua, &root_dir, &tools_dir, &exe_name) + } + + fn get_exe_context() -> Result<(PathBuf, PathBuf, PathBuf, PathBuf, String), Error> { + let current_exe = env::current_exe().map_err(|e| { + Error::new( + ErrorKind::Other, + format!("获取代理程序(shim)路径失败: {}", e), + ) + })?; + + let exe_dir = current_exe + .parent() + .unwrap_or_else(|| Path::new("")) + .to_path_buf(); + // root_dir 是 bin 的上一级(项目的根目录) + let root_dir = exe_dir + .parent() + .unwrap_or_else(|| Path::new("")) + .to_path_buf(); + // tools_dir 是 root 下的 tools 目录 + let tools_dir = root_dir.join("tools"); + + let exe_name = current_exe + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or_default() + .to_lowercase(); + + Ok((current_exe, exe_dir, root_dir, tools_dir, exe_name)) + } + /// 执行 Lua 脚本文件并返回 Table + fn evaluate_lua_script(lua: &Lua, config_file: &Path) -> Result { + let code = fs::read_to_string(config_file)?; // 这里不安全需要优化 + + lua.load(&code) + .set_name(config_file.to_string_lossy().into_owned()) + .eval::
() + .map_err(|e| { + Error::new( + ErrorKind::InvalidData, + format!("Lua 脚本解析错误 ({}): {}", config_file.display(), e), + ) + }) + } + /// 利用 mlua 原生类型转换,将 Lua Table 映射为 Rust Shim 结构体 + fn from_table(table: Table) -> Result { + let path_str: String = table + .get("path") + .map_err(|_| Error::new(ErrorKind::InvalidData, "配置文件中缺少必需的 'path' 字段"))?; + let target_path = PathBuf::from(path_str); + + let args: Option> = table.get("args").ok(); + let envs: Option> = table.get("env").ok(); + + Ok(Self { + target_path, + args, + envs, + }) + } + fn resolve_config( + lua: &Lua, + root_dir: &Path, + tools_dir: &Path, + exe_name: &str, + ) -> Result { + // 1. 尝试全局统一入口 shims.lua + let entry_config = root_dir.join("shims.lua"); + if entry_config.exists() { + let root_table = Self::evaluate_lua_script(lua, &entry_config)?; + + // 如果 shims.lua 中存在以当前程序名命名的配置项,直接接管并返回 + if let Ok(Value::Table(v)) = root_table.get::(exe_name) { + return Self::from_table(v); + } + // 如果 shims.lua 存在但未接管 exe_name,代码会自然“穿透”流转到下方寻找独立配置 + } + + // 2. 降级探测特定同名独立配置文件(优先 tools/{exe_name}.lua,次选 root/{exe_name}.lua) + let specific_config_tools = tools_dir.join(format!("{}.lua", exe_name)); + let specific_config_root = root_dir.join(format!("{}.lua", exe_name)); + + let specific_config = if specific_config_tools.exists() { + Some(specific_config_tools) + } else if specific_config_root.exists() { + Some(specific_config_root) + } else { + None + }; + + if let Some(config_file) = specific_config { + let config_table = Self::evaluate_lua_script(lua, &config_file)?; + return if config_table.contains_key("path").unwrap_or(false) { + Self::from_table(config_table) + } else { + Err(Error::new( + ErrorKind::InvalidData, + format!( + "独立配置文件 [{}] 必须包含 'path' 字段", + config_file.display() + ), )) - } - }; - let args = kvs.get("args").map(|a| { - a.split_whitespace() - .map(|s| s.to_string()) - .collect::>() - }); - Ok(Self { target_path, args }) + }; + } + + // 3. 两阶段探测均失败后的统一报错 + Err(Error::new( + ErrorKind::NotFound, + format!( + "未找到关于 '{}' 的配置。请确保在 shims.lua 中进行了定义,或者存在独立的 {}.lua 配置文件。", + exe_name, exe_name + ), + )) } } - -fn get_shim_file_path() -> Result { - let mut current_exe = env::current_exe().map_err(|e| { - Error::new( - ErrorKind::Other, - format!("acquiring shim executable path: {}", e), - ) - })?; - if !current_exe.set_extension("shim") { - return Err(Error::new( - ErrorKind::Other, - format!("{} is not a file", current_exe.to_string_lossy()), - )); - } - Ok(current_exe) -} -use unicode_bom::Bom; -fn parse_shim_file(shim_path: &Path) -> Result, Error> { - let mut kvs = HashMap::new(); - - let raw_content = fs::read_to_string(shim_path).map_err(|e| { - Error::new( - ErrorKind::Other, - format!("reading {}: {}", shim_path.to_string_lossy(), e), - ) - })?; - //NOTE: expedient trick for utf-8 with bom - let bom = Bom::from(raw_content.as_bytes()); - for line in raw_content[bom.len()..] - .lines() - .filter(|l| !l.trim().is_empty()) - { - let mut components = line.split("="); - let key = match components.next() { - Some(k) => k.trim(), - None => { - return Err(Error::new( - ErrorKind::InvalidData, - format!("invalid line in shim file: {}", line), - )); - } - }; - let value = match components.next() { - Some(v) => v.trim(), - None => { - return Err(Error::new( - ErrorKind::InvalidData, - format!("invalid line in shim file: {}", line), - )); - } - }; - kvs.insert(key.to_string(), value.to_string()); - } - - Ok(kvs) -}