- 将 MirrorTarget.target 改造为 TargetConfig 枚举(支持 Static 与 Versioned) - TargetConfig 独立实现 FromLua,解耦反序列化与业务校验 - to_command 调整为显式接收执行路径,简化构建逻辑 - 增加未选择版本时的前置拦截与友好提示
128 lines
5.0 KiB
Rust
128 lines
5.0 KiB
Rust
use std::ffi::OsString;
|
||
use mlua::{FromLua, Lua, LuaString, Value};
|
||
use std::path::PathBuf;
|
||
use crate::utils::lua_string_2_os_string;
|
||
use crate::validation_error;
|
||
use crate::validators::ensure_no_nul;
|
||
|
||
/// 目标可执行文件的定位配置(支持静态路径与多版本 Table)
|
||
#[derive(Debug, Clone)]
|
||
pub enum TargetConfig{
|
||
/// 静态单版本工具
|
||
Static(PathBuf),
|
||
/// 多版本管理
|
||
Versioned{
|
||
/// 工具主根目录(供 mr use 扫描候选版本)
|
||
home:PathBuf,
|
||
/// 版本真相源文件名(如 ".version",可选,默认 ".version")
|
||
version_file:Option<String>,
|
||
/// 当前激活版本的可执行文件完整路径(若未激活或不存在则为 None)
|
||
current:Option<PathBuf>,
|
||
}
|
||
}
|
||
impl Default for TargetConfig {
|
||
fn default() -> Self {
|
||
TargetConfig::Static(PathBuf::default())
|
||
}
|
||
}
|
||
impl TargetConfig {
|
||
/// 垫片程序(mirror-shim)专用快速通道:提取最终可执行路径
|
||
///
|
||
/// - Static: 返回自身路径
|
||
/// - Versioned: 返回 current 路径(若未激活则返回 None)
|
||
|
||
pub fn executable_path(&self) -> Option<&PathBuf> {
|
||
match self {
|
||
TargetConfig::Static(path) => Some(path),
|
||
TargetConfig::Versioned { current, .. } => current.as_ref(),
|
||
}
|
||
}
|
||
|
||
/// 管理程序(mr-cli)专用辅助方法:判断是否支持多版本
|
||
pub fn is_versioned(&self) -> bool {
|
||
matches!(self, TargetConfig::Versioned { .. })
|
||
}
|
||
}
|
||
|
||
impl FromLua for TargetConfig {
|
||
fn from_lua(value: Value, _lua: &Lua) -> mlua::Result<Self> {
|
||
match value {
|
||
// 分支 1:静态单版本工具 target = "..."
|
||
Value::String(s) => {
|
||
let os_str = lua_string_2_os_string(&s)?;
|
||
ensure_no_nul(&"target", &os_str)?;
|
||
Ok(TargetConfig::Static(PathBuf::from(os_str)))
|
||
}
|
||
|
||
// 分支 2:多版本管理工具 target = { home = "...", ... }
|
||
Value::Table(table) => {
|
||
// 1. home (必填字段)
|
||
let home = match table.get::<Option<Value>>("home")? {
|
||
Some(Value::String(s)) => {
|
||
let os_str = lua_string_2_os_string(&s)?;
|
||
ensure_no_nul(&"target.home", &os_str)?;
|
||
PathBuf::from(os_str)
|
||
}
|
||
None | Some(Value::Nil) => {
|
||
return Err(validation_error("多版本 target 表缺少必填字段 'home'"));
|
||
}
|
||
Some(other) => {
|
||
return Err(validation_error(format!(
|
||
"target.home 需为路径字符串,实际类型是 {}",
|
||
other.type_name()
|
||
)));
|
||
}
|
||
};
|
||
|
||
// 2. version_file (选填字段,未填或 nil 时为 None)
|
||
let version_file = match table.get::<Option<Value>>("version_file")? {
|
||
Some(Value::String(s)) => {
|
||
let os_str = lua_string_2_os_string(&s)?;
|
||
ensure_no_nul(&"target.version_file", &os_str)?;
|
||
Some(os_str.to_string_lossy().into_owned())
|
||
}
|
||
None | Some(Value::Nil) => None,
|
||
Some(other) => {
|
||
return Err(validation_error(format!(
|
||
"target.version_file 需为文件名字符串,实际类型是 {}",
|
||
other.type_name()
|
||
)));
|
||
}
|
||
};
|
||
|
||
// 3. current (选填/可为 nil,表示未激活或当前未指定版本)
|
||
let current = match table.get::<Option<Value>>("current")? {
|
||
Some(Value::String(s)) => {
|
||
let os_str = lua_string_2_os_string(&s)?;
|
||
ensure_no_nul(&"target.current", &os_str)?;
|
||
Some(PathBuf::from(os_str))
|
||
}
|
||
// Lua 侧 ver 为 nil 时,current 拼出的也是 nil,此时合法解析为 None
|
||
None | Some(Value::Nil) => None,
|
||
Some(other) => {
|
||
return Err(validation_error(format!(
|
||
"target.current 需为有效路径字符串或 nil,实际类型是 {}",
|
||
other.type_name()
|
||
)));
|
||
}
|
||
};
|
||
|
||
Ok(TargetConfig::Versioned {
|
||
home,
|
||
version_file,
|
||
current,
|
||
})
|
||
}
|
||
|
||
Value::Nil => Err(validation_error(
|
||
"缺少必填字段 target(应为字符串路径或多版本配置表)",
|
||
)),
|
||
other => Err(validation_error(format!(
|
||
"target 需为有效路径字符串或多版本配置表,实际类型是 {}",
|
||
other.type_name()
|
||
))),
|
||
}
|
||
}
|
||
}
|
||
|