refactor(core,shim): 统一基于 thiserror 的错误体系
- 移除 anyhow,使用 thiserror 定义强类型 Error 枚举 - 支持 #[from] 自动转换 mlua::Error,关键路径保留源码与上下文 - LuaRuntimeBuilder 采用惰性错误收集,简化链式初始化调用 - MirrorRegistry 实现规范化 FromLuaConversionError 错误转换
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
use crate::mirror::MirrorRegistry;
|
||||
use crate::sys::{
|
||||
ERROR_ELEVATION_REQUIRED, EXIT_FAILED_LOAD_SHIM, EXIT_FAILED_SPAWN_PROG, EXIT_FAILED_WAIT_PROG,
|
||||
EXIT_PROG_TERMINATED, execute_elevated,
|
||||
};
|
||||
use mirror_core::{Layout, LuaRuntime, register_basics};
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::process::exit;
|
||||
use tracing::{error, warn};
|
||||
|
||||
pub fn run(layout: &Layout) -> i32 {
|
||||
let global_config = &layout.base_dir.join("mirror.lua");
|
||||
|
||||
if !global_config.is_file() {
|
||||
error!("主配置文件不存在: {}", global_config.display());
|
||||
return EXIT_FAILED_LOAD_SHIM;
|
||||
}
|
||||
|
||||
let runtime = match LuaRuntime::builder()
|
||||
.with(|lua| register_basics(lua, &layout))
|
||||
.build()
|
||||
{
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
error!("初始化 Lua 失败: {:#}", e);
|
||||
return EXIT_FAILED_LOAD_SHIM;
|
||||
}
|
||||
};
|
||||
|
||||
let registry: MirrorRegistry = match runtime.eval_script(&global_config) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
error!("加载命令配置 [{}] 失败: {:#}", global_config.display(), e);
|
||||
return EXIT_FAILED_LOAD_SHIM;
|
||||
}
|
||||
};
|
||||
|
||||
let target_name = layout.target_name.as_str();
|
||||
|
||||
let mr = match registry.targets.get(target_name) {
|
||||
Some(m) => m,
|
||||
None => {
|
||||
warn!("未在 mirror.lua 中找到目标配置 [{}]", target_name);
|
||||
return EXIT_FAILED_LOAD_SHIM;
|
||||
}
|
||||
};
|
||||
|
||||
// 3. 解析调用参数与代理 Mirror 配置
|
||||
let calling_args: Vec<_> = env::args_os().skip(1).collect();
|
||||
|
||||
let combined_args = mr.args.iter().chain(calling_args.iter());
|
||||
|
||||
// 构建 Command:复用 ShimConfig::to_command(含 args/env 注入),避免重复逻辑
|
||||
let mut cmd = mr.to_command(combined_args);
|
||||
|
||||
let mut child = match cmd.spawn() {
|
||||
Ok(v) => v,
|
||||
Err(e) if e.raw_os_error() == Some(ERROR_ELEVATION_REQUIRED) => {
|
||||
// 提权回退时需要完整参数:配置默认参数 + 调用方透传参数
|
||||
let elevated_args: Vec<OsString> = cmd.get_args().map(|s| s.to_os_string()).collect();
|
||||
|
||||
exit(execute_elevated(&mr.target, &elevated_args, Some(&mr.env)))
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"启动目标程序 [{}] 时发生错误: {}",
|
||||
mr.target.to_string_lossy(),
|
||||
e
|
||||
);
|
||||
return EXIT_FAILED_SPAWN_PROG;
|
||||
}
|
||||
};
|
||||
// 5. 等待子进程退出
|
||||
match child.wait() {
|
||||
Ok(status) => status.code().unwrap_or(EXIT_PROG_TERMINATED),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"等待目标程序 [{}] 执行完毕时发生错误: {}",
|
||||
mr.target.to_string_lossy(),
|
||||
e
|
||||
);
|
||||
EXIT_FAILED_WAIT_PROG
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-59
@@ -1,25 +1,25 @@
|
||||
use mirror_core::{init_logging_from, Layout, };
|
||||
use crate::mirror::Mirror;
|
||||
use std::ffi::OsString;
|
||||
use std::{env, process::exit};
|
||||
use anyhow::Context;
|
||||
use tracing::{debug, error};
|
||||
use crate::sys::*;
|
||||
use anyhow::Context;
|
||||
use mirror_core::{Layout, init_logging_from};
|
||||
use std::{env, process::exit};
|
||||
use tracing::debug;
|
||||
|
||||
pub mod sys;
|
||||
mod executor;
|
||||
pub mod mirror;
|
||||
pub mod validators;
|
||||
pub mod sys;
|
||||
pub mod utils;
|
||||
pub mod validators;
|
||||
|
||||
fn main() {
|
||||
let current_exe = match env::current_exe().context("获取代理程序路径失败"){
|
||||
let current_exe = match env::current_exe().context("获取代理程序路径失败") {
|
||||
Ok(exe) => exe,
|
||||
Err(e) => {
|
||||
eprintln!("垫片启动失败: {:#}", e);
|
||||
exit(EXIT_FAILED_LOAD_SHIM);
|
||||
}
|
||||
};
|
||||
let layout =match Layout::from(¤t_exe){
|
||||
|
||||
let layout = match Layout::from(¤t_exe) {
|
||||
Ok(l) => l,
|
||||
Err(e) => {
|
||||
eprintln!("解析程序目录布局失败: {:#}", e);
|
||||
@@ -27,7 +27,6 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//初始化日志:输出到 stderr,避免污染 shim 子进程的 stdout
|
||||
let _guard = init_logging_from(&layout);
|
||||
|
||||
@@ -41,52 +40,6 @@ fn main() {
|
||||
// 2. 注册 Windows 控制台信号
|
||||
set_console_ctrl_handler();
|
||||
|
||||
// 3. 解析调用参数与代理 Mirror 配置
|
||||
let calling_args: Vec<_> = env::args_os().skip(1).collect();
|
||||
let mr = match Mirror::load(&layout) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("加载代理(mirror)配置时发生错误: {}", e);
|
||||
exit(EXIT_FAILED_LOAD_SHIM);
|
||||
}
|
||||
};
|
||||
let combined_args = mr.args.iter().chain(calling_args.iter());
|
||||
|
||||
// 构建 Command:复用 ShimConfig::to_command(含 args/env 注入),避免重复逻辑
|
||||
let mut cmd = mr.to_command(combined_args);
|
||||
|
||||
let mut child = match cmd.spawn() {
|
||||
Ok(v) => v,
|
||||
Err(e) if e.raw_os_error() == Some(ERROR_ELEVATION_REQUIRED) => {
|
||||
// 提权回退时需要完整参数:配置默认参数 + 调用方透传参数
|
||||
let elevated_args: Vec<OsString> = cmd.get_args().map(|s| s.to_os_string()).collect();
|
||||
|
||||
exit(execute_elevated(
|
||||
&mr.target,
|
||||
&elevated_args,
|
||||
Some(&mr.env),
|
||||
))
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"启动目标程序 [{}] 时发生错误: {}",
|
||||
mr.target.to_string_lossy(),
|
||||
e
|
||||
);
|
||||
exit(EXIT_FAILED_SPAWN_PROG);
|
||||
}
|
||||
};
|
||||
// 5. 等待子进程退出
|
||||
let status = match child.wait() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!(
|
||||
"等待目标程序 [{}] 执行完毕时发生错误: {}",
|
||||
mr.target.to_string_lossy(),
|
||||
e
|
||||
);
|
||||
exit(EXIT_FAILED_WAIT_PROG);
|
||||
}
|
||||
};
|
||||
exit(status.code().unwrap_or(EXIT_PROG_TERMINATED))
|
||||
let code = executor::run(&layout);
|
||||
exit(code);
|
||||
}
|
||||
|
||||
+41
-61
@@ -1,6 +1,8 @@
|
||||
use mirror_core::error::validation_error;
|
||||
use crate::validators::LuaValidator;
|
||||
use anyhow::{Context, Result, bail};
|
||||
use mirror_core::validate_command_name;
|
||||
use mirror_core::{Layout, LuaRuntime};
|
||||
use mirror_core::{conversion_err, validation_error};
|
||||
use mlua::{FromLua, Lua, Table, Value};
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
@@ -8,89 +10,66 @@ use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use tracing::{debug, trace};
|
||||
use mirror_core::{Layout, LuaRuntime};
|
||||
use mirror_core::validate_command_name;
|
||||
|
||||
struct MasterMirror{
|
||||
master:HashMap<String,Mirror>,
|
||||
pub struct MirrorRegistry {
|
||||
pub targets: HashMap<String, MirrorTarget>,
|
||||
}
|
||||
|
||||
impl FromLua for MasterMirror {
|
||||
impl FromLua for MirrorRegistry {
|
||||
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
|
||||
let root_tbl = match value {
|
||||
Value::Table(t) => t,
|
||||
other => {
|
||||
return Err(validation_error(format!(
|
||||
"mirror 注册表的顶层配置必须是 Table,实际检测到: {}",
|
||||
other.type_name()
|
||||
)));
|
||||
return Err(conversion_err(
|
||||
other.type_name(),
|
||||
"MirrorRegistry",
|
||||
format!(
|
||||
"mirror 全局注册表顶层必须是 Table,实际检测到: {}",
|
||||
other.type_name()
|
||||
),
|
||||
));
|
||||
}
|
||||
};
|
||||
let mut master = HashMap::new();
|
||||
let mut targets = HashMap::new();
|
||||
for pair in root_tbl.pairs::<Value, Value>() {
|
||||
let (cmd_name, cmd_entry) = pair?;
|
||||
let (key, entry) = pair?;
|
||||
|
||||
let cmd_name = validate_command_name(&cmd_name)?;
|
||||
let target_name = validate_command_name(&key)?;
|
||||
// 2. 校验 entry 是否是 Table
|
||||
if !cmd_entry.is_table() {
|
||||
return Err(validation_error(format!(
|
||||
"子命令 '{cmd_name}' 的配置必须是 Table,实际检测到: {}",
|
||||
cmd_entry.type_name()
|
||||
)));
|
||||
if !entry.is_table() {
|
||||
return Err(conversion_err(
|
||||
entry.type_name(),
|
||||
"MirrorRegistry",
|
||||
format!(
|
||||
"目标工具 '{target_name}' 的配置必须是 Table,实际检测到: {}",
|
||||
entry.type_name()
|
||||
),
|
||||
));
|
||||
}
|
||||
// 3. 直接交由子命令解析,外层负责补充错误上下文
|
||||
let cmd_def = Mirror::from_lua(cmd_entry, lua).map_err(|err| {
|
||||
validation_error(format!("子命令 '{cmd_name}' 配置解析失败:\n{err}"))
|
||||
let target_spec = MirrorTarget::from_lua(entry, lua).map_err(|err| {
|
||||
validation_error(format!("目标工具 '{target_name}' 配置解析失败:\n{err}"))
|
||||
})?;
|
||||
|
||||
master.insert(cmd_name, cmd_def);
|
||||
targets.insert(target_name, target_spec);
|
||||
}
|
||||
Ok(Self { master })
|
||||
}}
|
||||
|
||||
|
||||
Ok(Self { targets })
|
||||
}
|
||||
}
|
||||
/// 单个目标程序的代理规格配置
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Mirror {
|
||||
pub struct MirrorTarget {
|
||||
/// 目标程序可执行文件路径
|
||||
pub target: PathBuf,
|
||||
/// 默认预设参数列表
|
||||
pub args: Vec<OsString>,
|
||||
/// 参数别名映射表(用于展开以 mr: 为前缀的别名)
|
||||
pub aliases: HashMap<String, Vec<OsString>>,
|
||||
/// 注入/覆盖的环境变量
|
||||
pub env: HashMap<String, OsString>,
|
||||
}
|
||||
|
||||
impl Mirror {
|
||||
pub fn load(layout: &Layout) -> Result<Self> {
|
||||
// 策略 1: 尝试加载全局配置文件 mirror.lua
|
||||
let global_config = layout.base_dir.join("mirror.lua");
|
||||
|
||||
if !global_config.is_file() {
|
||||
bail!("主配置文件不存在: {}", global_config.display());
|
||||
}
|
||||
|
||||
let runtime = LuaRuntime::new(layout)?;
|
||||
|
||||
let root_table: Table = runtime.eval_script(&global_config)?;
|
||||
|
||||
let target_name = layout.target_name.as_str();
|
||||
|
||||
// 检查 mirror.lua 中是否存在以 target_name 命名的 Table 节点
|
||||
let target_val: Mirror = root_table
|
||||
.get::<Option<Mirror>>(target_name)
|
||||
.with_context(|| format!("读取目标配置 [{}] 失败", target_name))?
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"在 'mirror.lua' 中未找到目标程序 [{}] 的配置注册",
|
||||
target_name
|
||||
)
|
||||
})?;
|
||||
debug!(
|
||||
target = %target_name,
|
||||
source = %global_config.display(),
|
||||
"成功从全局配置文件中匹配到目标工具"
|
||||
);
|
||||
|
||||
Ok(target_val)
|
||||
}
|
||||
|
||||
impl MirrorTarget {
|
||||
/// 1. 核心参数路由解析(Delegation 到 Spec 的路由逻辑)
|
||||
fn resolve_args<I, S>(&self, args: I) -> Vec<OsString>
|
||||
where
|
||||
@@ -147,7 +126,8 @@ impl Mirror {
|
||||
cmd
|
||||
}
|
||||
}
|
||||
impl FromLua for Mirror {
|
||||
|
||||
impl FromLua for MirrorTarget {
|
||||
fn from_lua(value: Value, _lua: &Lua) -> mlua::Result<Self> {
|
||||
// 脚本返回必须是一个 Table 变体
|
||||
let table = match value {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use mirror_core::error::validation_error;
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use mirror_core::validation_error;
|
||||
use mlua::{LuaString, Table, Value};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ffi::OsString;
|
||||
@@ -7,8 +7,8 @@ use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
// use tinyjson::JsonValue;
|
||||
use tracing::debug;
|
||||
use crate::utils::{lua_string_2_os_string, parse_tokens};
|
||||
use tracing::debug;
|
||||
|
||||
/// Lua 值校验器:针对不同上下文定义校验规则
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -471,10 +471,10 @@ impl LuaValidator {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Mirror;
|
||||
use crate::mirror::MirrorTarget;
|
||||
use mlua::{FromLua, Lua};
|
||||
|
||||
fn parse(src: &str) -> mlua::Result<Mirror> {
|
||||
fn parse(src: &str) -> mlua::Result<MirrorTarget> {
|
||||
let lua = Lua::new();
|
||||
// 模拟 runtime 注入的 get_env:返回按平台分隔符拆分的段数组(空变量返回空表)
|
||||
let get_env = lua
|
||||
@@ -493,7 +493,7 @@ mod tests {
|
||||
lua.globals().set("get_env", get_env).unwrap();
|
||||
|
||||
let value = lua.load(src).eval::<Value>()?;
|
||||
let t = Mirror::from_lua(value, &lua);
|
||||
let t = MirrorTarget::from_lua(value, &lua);
|
||||
println!("读取出的数据:{:?}", t.clone()?);
|
||||
t
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user