接 M110 的 pack 模型与安全闸,做导入:三格式 → 实例目录,落 D10 §2 三条硬要求。 core(`addon::import`): - 三方合并:`merge::decide` 判据表(磁盘 × 基线 × 包内),base 侧落在 `<实例根>/.ULL/pack-baseline.json`;无基线时磁盘同名文件算用户已有内容 ⇒ 不覆盖。 冲突逐条进报告(user_modified / user_deleted / pre_existing + 可选处置)。 - 两阶段:先 overrides 再补远端;失败分级只有一条判据(是不是"某个文件搞不到"): 缺文件 ⇒ Partial;穿越/符号链接/限额/清单坏/摘要不符/IO ⇒ Failed 且整包回滚。 - 事务:`Staging` 临时目录 + `CommitGuard`(全新导入 rename 整目录、合并态 journal 逆序 回滚、Drop 即回滚),基线作为最后一个暂存文件同生共死。 - 三格式语义:mrpack `client-overrides` 胜 `overrides`、`server-overrides` 不解压; `.disabled`/`.old` 按路径后缀识别并原样保留;MultiMC `formatVersion != 1` 整份拒绝、 非加载器 component 逐项进报告;CF `classId` 认不出就不猜。 - 加载器只做翻译(`LoaderTarget`:kind + 原版 + 加载器版本 + 版本 id,含 CF NeoForge `1.20.1-47.1.106` 的 MC 前缀剥离),网络全部经注入的 `PackFetcher` (生产实现 `DownloadEngineFetcher` 只走 core::download,无第二个 reqwest client)。 - 报告:三态 + 逐项原因 + 稳定文案 key 表(`report::keys`,含 ALL 守门表)。 CLI(`ull pack import <file> [--target] [--name] [--overwrite] [--no-register]`): - 退出码三态 0 / 3 / 1(`Outcome::exit_code` + runner 透传,其它命令仍 0); - `--json` 恒有信封(`data.status` / `data.error.code` / `data.stages`),Failed 不抛栈。 测试:29 条 core(三格式端到端、合并 6 条、必须先失败 8 条含双检互补样本与确定性 提交失败回滚、报告契约 5 条)+ 5 条 CLI(三态退出码、JSON 契约、既有命令回归、help)。 fmt / clippy(core+cli, -D warnings) 绿;文件全部 ≤400 行。
57 lines
2.2 KiB
Rust
57 lines
2.2 KiB
Rust
//! 进程入口的可测试形态:解析 → 分发 → 打印 → 退出码。
|
|
//!
|
|
//! `main` 只做两件事:把真实 stdio 传进来、把返回码交给进程。
|
|
|
|
use std::io::Write;
|
|
|
|
use crate::cli;
|
|
use crate::commands;
|
|
use crate::error::EXIT_OK;
|
|
use crate::output;
|
|
|
|
/// 跑一条命令行,返回进程退出码(0 / 1 / 2)。
|
|
///
|
|
/// `args` 不含 argv[0]。`--json` 与 `--quiet` 在解析失败时也要生效,
|
|
/// 因此这里先扫一遍原始参数,而不是依赖解析结果。
|
|
pub fn run(args: &[String], out: &mut dyn Write, err: &mut dyn Write) -> i32 {
|
|
let json = args.iter().any(|arg| arg == "--json");
|
|
let quiet = args.iter().any(|arg| arg == "--quiet" || arg == "-q");
|
|
|
|
let invocation = match cli::parse(args) {
|
|
Ok(invocation) => invocation,
|
|
Err(error) => {
|
|
let _ = output::print_error(&error, json, out, err);
|
|
return error.exit_code();
|
|
}
|
|
};
|
|
|
|
// `ull agent serve` 提前分流:stdio 形态下 stdout 是 JSON-RPC 流,
|
|
// 任何一行额外输出(包括返回后的 Report)都会让客户端解析失败。
|
|
if let cli::Command::AgentServe(serve_args) = &invocation.command {
|
|
return match crate::commands::agent::run_serve_with(&invocation.globals, serve_args, err) {
|
|
Ok(code) => code,
|
|
Err(error) => {
|
|
let _ = output::print_error(&error, json, out, err);
|
|
error.exit_code()
|
|
}
|
|
};
|
|
}
|
|
|
|
match commands::run(&invocation) {
|
|
Ok(outcome) => {
|
|
if let Err(write_err) = output::print_report(&outcome, json, quiet, out, err) {
|
|
// 输出写不出去(管道早闭)不再折腾,直接按内部错误退出。
|
|
let _ = writeln!(err, "错误: 写输出失败:{write_err}");
|
|
return crate::error::EXIT_INTERNAL;
|
|
}
|
|
// 命令可以自己指定退出码(`ull pack import` 的三态:0 / 3 / 1);
|
|
// 没指定就是 0——其它命令的行为不变。
|
|
outcome.exit_code.unwrap_or(EXIT_OK)
|
|
}
|
|
Err(error) => {
|
|
let _ = output::print_error(&error, json, out, err);
|
|
error.exit_code()
|
|
}
|
|
}
|
|
}
|