use std::collections::HashMap; use std::path::PathBuf; use std::process::Command; use mlua::{FromLua, Lua, Table, Value}; #[derive(Debug, Clone)] pub struct ShimConfig { pub target_path: PathBuf, pub args: Option>, pub envs: Option>, } 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 { match value { Value::Table(table) => { let path_str: String = table.get("path")?; let args: Option> = table.get("args")?; // 解析 env Table let mut envs_map = HashMap::new(); if let Ok(env_table) = table.get::("env") { // 获取当前系统的路径分隔符(Windows 为 ";",Unix 为 ":") #[cfg(windows)] let sep = ";"; #[cfg(not(windows))] let sep = ":"; for pair in env_table.pairs::() { 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 = arr // 将 arr 作为序列(数组)处理,每个元素转为 String .sequence_values::() .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()), }), } } }