feat(env): 支持环境变量数组跨平台自动拼接
- 支持在 Lua 中配置数组格式的环境变量(如 PATH),由 Rust 按 OS 自动使用 `;` 或 `:` 拼接 - 保持普通字符串格式环境变量的直接覆盖逻辑 - 新增 `ShimConfig::to_command()` 用于快速创建 Command 对象 - 新增 error.rs 明确错误类型 - 新增 build.rs 优化编译
This commit is contained in:
21
Cargo.lock
generated
21
Cargo.lock
generated
@@ -201,6 +201,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"fs-err",
|
||||
"mlua",
|
||||
"thiserror",
|
||||
"unicode-bom",
|
||||
"winapi",
|
||||
]
|
||||
@@ -260,6 +261,26 @@ dependencies = [
|
||||
"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"
|
||||
|
||||
@@ -13,6 +13,7 @@ panic = "abort"
|
||||
[dependencies]
|
||||
fs-err = "3.3.1"
|
||||
unicode-bom = "2.0.3"
|
||||
thiserror={version = "2.0.20"}
|
||||
mlua = { version = "0.12.0", features = ["lua54", "vendored"] }
|
||||
winapi = { version = "0.3", features = [
|
||||
"wincon",
|
||||
|
||||
47
build.rs
Normal file
47
build.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// CARGO_MANIFEST_DIR -> 项目根目录路径
|
||||
// CARGO_TARGET_DIR -> 用户自定义了 target 目录,使用该变量
|
||||
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
let target_dir = env::var("CARGO_TARGET_DIR")
|
||||
.ok()
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| PathBuf::from(&manifest_dir).join("target"));
|
||||
|
||||
// PROFILE -> 获取当前构建配置文件(debug/release)
|
||||
let profile = env::var("PROFILE").unwrap();
|
||||
// let output_dir = target_dir.join(&profile);
|
||||
|
||||
// 在 target/debug/ 或 target/release/ 下创建 bin2 目录
|
||||
let output_dir = target_dir.join(&profile).join("bin2");
|
||||
|
||||
|
||||
// cargo:rerun-if-changed -> 当指定文件变化时,重新运行 xxx
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=shims.lua");
|
||||
println!("cargo:rustc-env=OUTPUT_DIR={}", output_dir.display());
|
||||
|
||||
// 创建目录
|
||||
let dirs = ["bin", "tools"];
|
||||
for dir in &dirs {
|
||||
let path = output_dir.join(dir);
|
||||
if !path.exists() {
|
||||
fs::create_dir_all(&path).expect(&format!("Failed to create {} directory", dir));
|
||||
println!("Created: {:?}", path);
|
||||
}
|
||||
}
|
||||
|
||||
// 复制文件
|
||||
let source = PathBuf::from(&manifest_dir).join("shims.lua");
|
||||
let destination = output_dir.join("shims.lua");
|
||||
|
||||
if source.exists() {
|
||||
fs::copy(&source, &destination).expect("Failed to copy shims.lua");
|
||||
println!("Copied shims.lua to: {:?}", destination);
|
||||
} else {
|
||||
panic!("shims.lua not found at: {:?}", source);
|
||||
}
|
||||
}
|
||||
19
shims.lua
19
shims.lua
@@ -2,6 +2,9 @@
|
||||
-- __SHIM_DIR__ 已经在 Rust 中注入完毕,并且是正斜杠格式 (例如 C:/Tools)
|
||||
local base_dir = __SHIM_DIR__
|
||||
|
||||
-- 1. 自定义局部变量,方便复用与后续维护
|
||||
local python_home = base_dir .. "/tools/python39"
|
||||
|
||||
return {
|
||||
---------------------------------------------------
|
||||
-- 1. 标准相对路径 + 正斜杠拼接 (最推荐,绿色便携)
|
||||
@@ -12,7 +15,7 @@ return {
|
||||
args = { "--no-pager" },
|
||||
-- 注入环境变量,使用 get_env 获取宿主机当前值
|
||||
env = {
|
||||
PATH = base_dir .. "/git/bin;" .. get_env("PATH")
|
||||
PATH = { base_dir .. "/git/bin", get_env("PATH") }
|
||||
}
|
||||
},
|
||||
|
||||
@@ -24,7 +27,19 @@ return {
|
||||
path = [[C:\Python310\python.exe]],
|
||||
args = { "-B" },
|
||||
-- 空字典也是合法的,等同于不设置
|
||||
env = {}
|
||||
env = {
|
||||
-- 普通字符串:Rust 直接新建/覆盖该环境变量
|
||||
PYTHON_HOME = python_home,
|
||||
HOME = base_dir .. "/home",
|
||||
|
||||
-- 数组列表:将 python_home 以及 python_home/Scripts 依次拼接到 PATH 前面
|
||||
PATH = {
|
||||
python_home,
|
||||
python_home .. "/Scripts",
|
||||
get_env("PATH")
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
---------------------------------------------------
|
||||
|
||||
27
src/error.rs
Normal file
27
src/error.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error; // 推荐引入 thiserror 库,若不使用可手动实现 std::fmt::Display
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ShimError {
|
||||
#[error("路径解析失败: {0}")]
|
||||
PathResolutionError(String),
|
||||
|
||||
#[error("获取环境信息失败: {0}")]
|
||||
EnvError(String),
|
||||
|
||||
#[error("配置文件未找到: {0}")]
|
||||
ConfigNotFound(String),
|
||||
|
||||
#[error("Lua 运行时/语法错误 [{file}]: {source}")]
|
||||
LuaExecutionError {
|
||||
file: String,
|
||||
#[source]
|
||||
source: mlua::Error,
|
||||
},
|
||||
|
||||
#[error("配置格式非法: {0}")]
|
||||
InvalidConfig(String),
|
||||
|
||||
#[error("IO 错误: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
@@ -7,6 +7,8 @@ use std::{
|
||||
ptr::null_mut,
|
||||
};
|
||||
mod shims;
|
||||
mod error;
|
||||
|
||||
use shims::Shim;
|
||||
|
||||
use winapi::{
|
||||
|
||||
361
src/shims.rs
361
src/shims.rs
@@ -1,4 +1,6 @@
|
||||
use mlua::{Error as LuaError, Lua, Table, Value};
|
||||
use crate::error::ShimError;
|
||||
use mlua::{Error as LuaError, FromLua, Lua, StdLib, Table, Value};
|
||||
use std::process::Command;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env, fs,
|
||||
@@ -6,38 +8,198 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
pub struct Shim {
|
||||
/// 将 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 ShimEnv {
|
||||
pub bin_dir: PathBuf,
|
||||
pub root_dir: PathBuf,
|
||||
pub tools_dir: PathBuf,
|
||||
pub target_name: String,
|
||||
}
|
||||
|
||||
impl ShimEnv {
|
||||
/// 提取当前代理程序的运行环境信息
|
||||
pub fn new(current_exe: PathBuf) -> Result<Self, ShimError> {
|
||||
let bin_dir = current_exe
|
||||
.parent()
|
||||
.ok_or_else(|| {
|
||||
ShimError::PathResolutionError(format!(
|
||||
"无法获取程序 [{}] 的父级 bin 目录",
|
||||
current_exe.display()
|
||||
))
|
||||
})?
|
||||
.to_path_buf();
|
||||
println!("bin_dir目录 {}", bin_dir.display());
|
||||
|
||||
let root_dir = bin_dir
|
||||
.parent()
|
||||
.ok_or_else(|| {
|
||||
ShimError::PathResolutionError(format!(
|
||||
"无法获取 bin 目录 [{}] 的父级 root 目录",
|
||||
bin_dir.display()
|
||||
))
|
||||
})?
|
||||
.to_path_buf();
|
||||
println!("root_dir 目录 {}", root_dir.display());
|
||||
|
||||
let tools_dir = root_dir.join("tools");
|
||||
println!("tools_dir 目录 {}", tools_dir.display());
|
||||
|
||||
let target_name = current_exe
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.ok_or_else(|| {
|
||||
ShimError::PathResolutionError(format!(
|
||||
"无法从路径 [{}] 提取有效的程序名称",
|
||||
current_exe.display()
|
||||
))
|
||||
})?
|
||||
.to_lowercase();
|
||||
println!("程序名称 {}", target_name);
|
||||
|
||||
Ok(Self {
|
||||
bin_dir,
|
||||
root_dir,
|
||||
tools_dir,
|
||||
target_name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ShimConfig {
|
||||
pub target_path: PathBuf,
|
||||
pub args: Option<Vec<String>>,
|
||||
pub envs: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
impl Shim {
|
||||
pub fn init() -> Result<Self, Error> {
|
||||
let (_exe_path, _exe_dir, root_dir, tools_dir, exe_name) = Self::get_exe_context()?;
|
||||
impl ShimConfig {
|
||||
/// 根据配置快速构建准备执行的 Command 对象
|
||||
pub fn to_command(&self) -> Command {
|
||||
let mut cmd = Command::new(&self.target_path);
|
||||
|
||||
if let Some(args) = &self.args {
|
||||
cmd.args(args);
|
||||
}
|
||||
|
||||
if let Some(envs) = &self.envs {
|
||||
for (key, val) in envs {
|
||||
// 直接应用环境变量(Lua 端已经处理好字符串拼接或列表合并)
|
||||
cmd.env(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
cmd
|
||||
}
|
||||
}
|
||||
|
||||
/// 实现 FromLua Trait,由 mlua 自动处理 Table 转换
|
||||
impl FromLua for ShimConfig {
|
||||
fn from_lua(value: Value, _lua: &Lua) -> mlua::Result<Self> {
|
||||
match value {
|
||||
Value::Table(table) => {
|
||||
let path_str: String = table.get("path")?;
|
||||
let args: Option<Vec<String>> = table.get("args")?;
|
||||
// 解析 env Table
|
||||
let mut envs_map = HashMap::new();
|
||||
if let Ok(env_table) = table.get::<Table>("env") {
|
||||
// 获取当前系统的路径分隔符(Windows 为 ";",Unix 为 ":")
|
||||
#[cfg(windows)]
|
||||
let sep = ";";
|
||||
#[cfg(not(windows))]
|
||||
let sep = ":";
|
||||
|
||||
for pair in env_table.pairs::<String, Value>() {
|
||||
let (k, v) = pair?;
|
||||
match v {
|
||||
// 情况 1: 普通字符串,如 HOME = "C:/path" -> 直接覆盖
|
||||
Value::String(s) => {
|
||||
envs_map.insert(k, s.to_str()?.to_string());
|
||||
}
|
||||
// 情况 2: 数组 Table,如 PATH = { bin_dir, get_env("PATH") }
|
||||
Value::Table(arr) => {
|
||||
let paths: Vec<String> = arr
|
||||
// 将 arr 作为序列(数组)处理,每个元素转为 String
|
||||
.sequence_values::<String>()
|
||||
.filter_map(|r| r.ok())
|
||||
.filter(|s| !s.is_empty()) // 过滤空串,防止生成不必要的连续 ;;
|
||||
.collect();
|
||||
|
||||
let combined = paths.join(sep);
|
||||
envs_map.insert(k, combined);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let envs = if envs_map.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(envs_map)
|
||||
};
|
||||
|
||||
Ok(ShimConfig {
|
||||
target_path: PathBuf::from(path_str),
|
||||
args,
|
||||
envs,
|
||||
})
|
||||
}
|
||||
_ => Err(mlua::Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "ShimConfig".into(),
|
||||
message: Some("Expected a Lua table".to_string()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
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 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('\\', "/");
|
||||
// 统一使用 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| Error::new(ErrorKind::Other, format!("注入 __SHIM_DIR__ 失败: {}", e)))?;
|
||||
.map_err(|e| ShimError::EnvError(e.to_string()))?;
|
||||
|
||||
// 2. 注册 get_env 获取环境变量的函数
|
||||
// 2. 安全暴露 get_env 供配置读取环境变量
|
||||
let get_env = lua
|
||||
.create_function(|_, key: String| -> Result<String, LuaError> {
|
||||
.create_function(|_, key: String| -> mlua::Result<String> {
|
||||
Ok(env::var(key).unwrap_or_default())
|
||||
})
|
||||
.map_err(|e| Error::new(ErrorKind::Other, format!("注册 get_env 失败: {}", e)))?;
|
||||
.map_err(|e| ShimError::EnvError(e.to_string()))?;
|
||||
|
||||
globals
|
||||
.set("get_env", get_env)
|
||||
.map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
|
||||
.map_err(|e| ShimError::EnvError(e.to_string()))?;
|
||||
|
||||
// 3. 增强 package.path,让 require 自动在当前目录及 conf.d 目录下寻找模块
|
||||
// 3. 配置 package.path,确保 require 行为正常
|
||||
if let Ok(package) = globals.get::<Table>("package") {
|
||||
if let Ok(path) = package.get::<String>("path") {
|
||||
let new_path = format!(
|
||||
@@ -48,119 +210,74 @@ impl Shim {
|
||||
}
|
||||
}
|
||||
|
||||
Self::resolve_config(&lua, &root_dir, &tools_dir, &exe_name)
|
||||
Ok(Self { lua })
|
||||
}
|
||||
|
||||
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),
|
||||
)
|
||||
})?;
|
||||
/// 执行指定脚本文件,直接返回完整的 Lua Table
|
||||
pub fn evaluate_lua_script(&self, path: &Path) -> Result<Table, ShimError> {
|
||||
let code = fs::read_to_string(path)?;
|
||||
|
||||
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<Table, Error> {
|
||||
let code = fs::read_to_string(config_file)?; // 这里不安全需要优化
|
||||
|
||||
lua.load(&code)
|
||||
.set_name(config_file.to_string_lossy().into_owned())
|
||||
self.lua
|
||||
.load(&code)
|
||||
.set_name(path.to_string_lossy())
|
||||
.eval::<Table>()
|
||||
.map_err(|e| {
|
||||
Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
format!("Lua 脚本解析错误 ({}): {}", config_file.display(), e),
|
||||
)
|
||||
.map_err(|e| ShimError::LuaExecutionError {
|
||||
file: path.display().to_string(),
|
||||
source: e,
|
||||
})
|
||||
}
|
||||
/// 利用 mlua 原生类型转换,将 Lua Table 映射为 Rust Shim 结构体
|
||||
fn from_table(table: Table) -> Result<Self, Error> {
|
||||
let path_str: String = table
|
||||
.get("path")
|
||||
.map_err(|_| Error::new(ErrorKind::InvalidData, "配置文件中缺少必需的 'path' 字段"))?;
|
||||
let target_path = PathBuf::from(path_str);
|
||||
|
||||
let args: Option<Vec<String>> = table.get("args").ok();
|
||||
let envs: Option<HashMap<String, String>> = 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<Self, Error> {
|
||||
// 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::<Value>(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()
|
||||
),
|
||||
))
|
||||
};
|
||||
}
|
||||
|
||||
// 3. 两阶段探测均失败后的统一报错
|
||||
Err(Error::new(
|
||||
ErrorKind::NotFound,
|
||||
format!(
|
||||
"未找到关于 '{}' 的配置。请确保在 shims.lua 中进行了定义,或者存在独立的 {}.lua 配置文件。",
|
||||
exe_name, exe_name
|
||||
),
|
||||
))
|
||||
/// 将 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()))
|
||||
}
|
||||
}
|
||||
pub struct Shim;
|
||||
|
||||
impl Shim {
|
||||
pub fn init() -> Result<ShimConfig, ShimError> {
|
||||
let current_exe = env::current_exe()
|
||||
.map_err(|e| Error::new(ErrorKind::Other, format!("获取代理程序路径失败: {}", e)))?;
|
||||
println!("当前目录 {}", current_exe.display());
|
||||
let shim_env = ShimEnv::new(current_exe)?;
|
||||
let runtime = LuaRuntime::new(&shim_env)?;
|
||||
|
||||
Self::resolve_config(&runtime, &shim_env)
|
||||
}
|
||||
|
||||
fn resolve_config(engine: &LuaRuntime, env: &ShimEnv) -> Result<ShimConfig, ShimError> {
|
||||
// 策略 1: 尝试加载全局配置文件 shims.lua
|
||||
let global_config = env.root_dir.join("shims.lua");
|
||||
if global_config.is_file() {
|
||||
let root_table = engine.evaluate_lua_script(&global_config)?;
|
||||
|
||||
// 检查 shims.lua 中是否存在以 target_name 命名的 Table 节点
|
||||
if let Ok(target_val) = root_table.get::<Value>(env.target_name.as_str()) {
|
||||
if matches!(target_val, Value::Table(_)) {
|
||||
return engine.parse_config(target_val);
|
||||
}
|
||||
}
|
||||
// 穿透:若全局配置文件存在但未包含当前程序的 key,继续向下探查
|
||||
}
|
||||
|
||||
// 策略 2: 降级寻找独立文件 ({exe}.lua),优先顺序:tools/ > root/
|
||||
let target_filename = format!("{}.lua", env.target_name);
|
||||
let candidates = [
|
||||
env.tools_dir.join(&target_filename),
|
||||
env.root_dir.join(&target_filename),
|
||||
];
|
||||
|
||||
for config_path in &candidates {
|
||||
if config_path.is_file() {
|
||||
let table = engine.evaluate_lua_script(config_path)?;
|
||||
return engine.parse_config(Value::Table(table));
|
||||
}
|
||||
}
|
||||
|
||||
// 策略 3: 所有查找失败,抛出错误
|
||||
Err(ShimError::ConfigNotFound(format!(
|
||||
"未找到关于 '{}' 的配置。请检查 shims.lua 或特定的 {}.lua 文件",
|
||||
env.target_name, env.target_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user