From f6b7759aa81bb0c5cecf0dea456579d26fafd121 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Thu, 5 Nov 2026 18:08:11 +0800 Subject: [PATCH] =?UTF-8?q?docs(m126=20r3):=20=E6=94=B6=E7=AA=84=E3=80=8C?= =?UTF-8?q?=E5=94=AF=E4=B8=80=E4=B8=80=E4=BB=BD=E3=80=8D=E7=9A=84=E8=AF=B4?= =?UTF-8?q?=E6=B3=95=EF=BC=8C=E5=B9=B6=E6=8A=8A=20=E2=91=A2=20=E7=9A=84?= =?UTF-8?q?=E6=89=BF=E8=AF=BA=E6=94=B9=E6=88=90=E7=9C=9F=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 纯文档,零逻辑改动(`git diff` 里每一行都是 `//!` / `///`)。 P2-1「唯一一份」不成立:`cli/src/cli/help.rs` 里 `ull help instances` 的词表行是 **会打印给用户看的第二份逐字副本**。四处说法收窄为「源头表在 core + 另一处逐字副本在 help.rs,由 `help_lists_every_stable_id` 按 `ALL` 遍历守着」:`game/loader.rs` 的模块 文档(原「生产代码里这七个串只有一处」)与 `as_str` doc(原「生产代码里唯一一份 id 字面量」)、`cli/commands/instances.rs` 的模块文档(原「全仓唯一一份 id 字面量」)与 `loader_json` doc(原「两样都只该有一份」)、`presenter/dto/instances.rs` 的 as_str doc 与契约测试 doc、`addon/loader/mod.rs` 的两处「唯一真相源」。 P2-2「③ 拦得住静默变原版」对显示路径不成立:`presenter/src/host/game.rs::dto_kind` 里 糊一支 `=> Vanilla`、同时把契约与 help 补齐,三个 crate 全绿而磁贴静默显示「原版」。 现在写的是「③ 拦的是**对外词表**(加种类、改词表都红),但**拦不住显示路径**,那处目前 没有守卫(已知缺口,另行记账)」。 顺带只做排版级的行数回收:loader.rs 400 → 397(≤400,仍留余量)。 门禁:cargo fmt --all --check → FMT_OK(本轮按要求只跑 fmt)。 --- crates/cli/src/commands/instances.rs | 14 +++++----- crates/core/src/addon/loader/mod.rs | 6 ++--- crates/core/src/game/loader.rs | 38 ++++++++++++++------------- crates/presenter/src/dto/instances.rs | 7 ++--- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/crates/cli/src/commands/instances.rs b/crates/cli/src/commands/instances.rs index 5e77503..857ea95 100644 --- a/crates/cli/src/commands/instances.rs +++ b/crates/cli/src/commands/instances.rs @@ -8,10 +8,12 @@ //! //! 每一行实例带一个 `loader` 对象:`{"kind": <稳定 id>, "version": <版本段>}`。它是 //! **`version_id` 的解析结果**,解析本身不在这里——[`unlucky_core::game::InstanceLoader::parse`] -//! 是全仓唯一的 id → 加载器入口(M122 收敛)。种类 → 稳定 id 的词表同样不在本 crate: -//! [`unlucky_core::game::LoaderKind::as_str`] 是全仓唯一一份 id 字面量(M126 收敛),这里直接 -//! 调它,**连翻译层都没有**——词表与 id 匹配一样,两处各算一遍就会漂(M120 的「刚装的 -//! Fabric 显示成原版」)。 +//! 是全仓唯一的 id → 加载器入口(M122 收敛)。种类 → 稳定 id 的词表也不在本 crate 的代码里: +//! [`unlucky_core::game::LoaderKind::as_str`] 是生产代码里 id 的**源头表**(M126 收敛),这里 +//! 直接调它,**连翻译层都没有**——词表与 id 匹配一样,两处各算一遍就会漂(M120 的「刚装的 +//! Fabric 显示成原版」)。生产代码里唯一的逐字副本是 `cli::help` 里 `ull help instances` 的 +//! 词表行(会打印给用户),由本模块的 `help_vocabulary_tests::help_lists_every_stable_id` +//! 按 `ALL` 遍历守着。 //! //! ## 约定一:`loader` 恒存在,原版是 `{"kind":"vanilla","version":""}` //! @@ -131,8 +133,8 @@ fn option_path(path: Option<&PathBuf>) -> Option { /// 版本 id → `loader` 对象(`{"kind": …, "version": …}`)。 /// /// 解析走 [`InstanceLoader::parse`](全仓唯一入口);种类 → 稳定 id 走 -/// [`unlucky_core::game::LoaderKind::as_str`](词表的唯一真相源,M126)。这里既**不写 id -/// 匹配**,也**不写词表**:两样都只该有一份,且都在 core。原版同样给出对象 +/// [`unlucky_core::game::LoaderKind::as_str`](词表的源头,M126)。这里既**不写 id +/// 匹配**,也**不写词表**:词表的源头只在 core(副本见模块文档)。原版同样给出对象 /// (`kind = vanilla`、`version = ""`),不缺键、不为 `null`——见模块文档的「约定一」。 fn loader_json(version_id: &str) -> Value { let parsed = InstanceLoader::parse(version_id); diff --git a/crates/core/src/addon/loader/mod.rs b/crates/core/src/addon/loader/mod.rs index a827521..a915f2d 100644 --- a/crates/core/src/addon/loader/mod.rs +++ b/crates/core/src/addon/loader/mod.rs @@ -64,7 +64,7 @@ pub enum LoaderKind { impl LoaderKind { /// 解析命令行 / 整合包清单里的加载器名(大小写不敏感)。 /// - /// 七个 id 的匹配**委派**给 [`crate::game::LoaderKind::parse`](词表唯一真相源,M126), + /// 七个 id 的匹配**委派**给 [`crate::game::LoaderKind::parse`](词表的源头,M126), /// 本方法只多收一个**输入别名** `neo-forge`:那是 Modrinth 的拼法(mrpack /// `dependencies` 的键、CF 包里也有这种写法),**只进不出**——[`Self::as_str`] 绝不产出 /// 它。识别得出但本启动器装不了的几家(原版 / OptiFine / LiteLoader)返回 `None`。 @@ -94,8 +94,8 @@ impl LoaderKind { /// 稳定字符串 id(进版本 id、JSON 输出与日志)。 /// - /// 词表**不在本模块**:[`crate::game::LoaderKind::as_str`] 是「种类 → 对外稳定 id」的唯一 - /// 真相源(M126)。这里只做**结构映射**(一个 id 字面量都没有)——安装侧拼出来的 + /// 词表**不在本模块**:[`crate::game::LoaderKind::as_str`] 是「种类 → 对外稳定 id」的源头 + /// (M126)。这里只做**结构映射**(一个 id 字面量都没有)——安装侧拼出来的 /// `--` 必须能被 [`crate::game::InstanceLoader::parse`] 认回来,委派让 /// 「两边各写一份、改一边忘了另一边」这件事物理上做不到。 #[must_use] diff --git a/crates/core/src/game/loader.rs b/crates/core/src/game/loader.rs index da9d86c..490a6d2 100644 --- a/crates/core/src/game/loader.rs +++ b/crates/core/src/game/loader.rs @@ -1,5 +1,5 @@ -//! 版本 id → 加载器:**全仓唯一**的 id 解析入口(M122),外加「种类 → 对外稳定 id」的 -//! **全仓唯一**命名词表(M126,见 [`LoaderKind::as_str`])。 +//! 版本 id → 加载器:**全仓唯一**的 id 解析入口(M122),外加「种类 → 对外稳定 id」的命名词表 +//! **源头**(M126,见 [`LoaderKind::as_str`];副本只有一处,见下)。 //! //! # 为什么要这一个模块 //! @@ -16,24 +16,25 @@ //! # 命名词表只有一张表(M126) //! //! 契约(0.2.0 已发布形状,**不许改名**):`vanilla` / `fabric` / `quilt` / `forge` / -//! `neoforge` / `optifine` / `liteloader`。生产代码里这七个串只有**一处**:下面那次 +//! `neoforge` / `optifine` / `liteloader`。**生产代码里这份词表有两处,都要改**:① 下面那次 //! `loader_kind_vocab!` 展开——枚举、`ALL`、`as_str`、`has_marker` 全从同一张表长出来,所以 -//! 「加了一支却漏进清单」物理上做不到(理由见宏的注释)。安装侧与 presenter 的 `as_str` / -//! `parse` 都是**委派**,CLI 直接调;唯一不在词表里的字面量是安装侧收的输入别名 `neo-forge` -//! (Modrinth 拼法,只进不出)。 +//! 「加了一支却漏进清单」物理上做不到(理由见宏的注释);② 会打印给用户看的 +//! `crates/cli/src/cli/help.rs` 里 `ull help instances` 的词表行——**逐字副本**、没有委派, +//! 由 CLI 的 `help_lists_every_stable_id`(按 `ALL` 遍历)守着。安装侧与 presenter 的 `as_str` / +//! `parse` 都是**委派**,CLI 的 `loader.kind` 直接调;唯一不进词表的字面量是安装侧收的输入别名 +//! `neo-forge`(Modrinth 拼法,只进不出)。 //! //! 加一家加载器要动:① `loader_kind_vocab!` 表里加一行(id + 认不认标记)→ 枚举 / `ALL` / //! `as_str` / `has_marker` / 解析顺序一起长出来,漏不了;② presenter 的 `to_core`·`from_core` //! **编不过**(逼你决定 DTO 怎么显示它),能装的话安装侧 `as_str`·`from_core` 也各差一支; -//! ③ 契约测试**会红**:presenter 的 `loader_kind_ids_are_the_released_contract` 把 core 的 -//! `ALL` 与独立手写的七个 id 做**集合比较**,CLI 的 `help_lists_every_stable_id` 按 `ALL` -//! 遍历 help 词表,`json_rows_*` 钉真二进制输出面——三处都逼你确认「对外词表真的要多一个 -//! id」,因为 0.2.0 已经发出去过。 +//! ③ 契约测试**会红**:presenter 的 `loader_kind_ids_are_the_released_contract` 拿 core 的 `ALL` +//! 与独立手写的七个 id 做**集合比较**,CLI 的 `help_lists_every_stable_id` 按 `ALL` 遍历 help +//! 词表,`json_rows_*` 钉真二进制输出面(0.2.0 已发出去过,所以这步是确认而不是可选)。 //! -//! ⚠️ 分工要看准:**编译器只拦「漏 match」**。新种类在三处消费方用最省事的 `=> Vanilla` 糊 -//! 过去是**编得过**的(那时它会被静默当成原版);真正拦住这件事的是 ③ 里那条集合断言,不是 -//! 编译器。**改名**(`neoforge` → `neo_forge`)同理:往返测试是绿的(词表与解析同源、改名自 -//! 洽),只有 ③ 会红——那里**故意**再抄一遍七个串。 +//! ⚠️ 分工要看准:**编译器只拦「漏 match」**,新种类在消费方用 `=> Vanilla` 糊过去编得过。 +//! ③ 那条集合断言拦的是**对外词表**,但**拦不住显示路径**:`presenter/src/host/game.rs::dto_kind` +//! 里糊一支 `=> Vanilla` 并把契约与 help 补齐后,三个 crate 全绿而磁贴静默显示「原版」——那处 +//! 目前**没有守卫**(已知缺口,另行记账)。**改名**同理:往返测试绿,只有 ③ 会红(故意再抄)。 //! //! # 解析规则(按整段前缀匹配,大小写不敏感,**最靠左的标记胜**) //! @@ -42,9 +43,8 @@ //! 段(`fabric-loader-…`)或同段里的 `_loader` 尾巴算标记的一部分,不是版本段; //! 2. 该段里标记之后剩下的部分,加上它右边的所有段,按 `-` 拼回版本段 //! (`_` 之类的原样保留,所以 `1.13.2-OptiFine_HD_U_E6` 的版本是 `HD_U_E6`); -//! 3. 例外:版本段有 **≥2 段** 且**最后一段**长得像 MC 版本(`1.20.1` 这种纯数字点号)时 -//! 砍掉最后一段——那是老 HMCL 的 `fabric-loader--` 写法。一段都不剩的 -//! `0.16.5` 不会被砍;`0.20.0-beta.9` 的末段不是数字,也不会被砍; +//! 3. 例外:版本段 **≥2 段**且末段像 MC 版本(`1.20.1` 这种纯数字点号)时砍掉末段——老 HMCL 的 +//! `fabric-loader--` 写法;`0.16.5`(只剩一段)与 `0.20.0-beta.9` 不受影响; //! 4. 一个标记都没找到 ⇒ 原版(版本段空串)。只把加载器写进版本 JSON、id 里没痕迹的实例 //! (整包导入时被重命名成 `我的整合包` 那种)会**诚实**返回「原版」——那不等于没装加载器。 @@ -80,7 +80,9 @@ macro_rules! loader_kind_vocab { /// 对外稳定 id(`--json` 的 `loader.kind`、GUI 词表、`--loader` 的取值)。 /// - /// **生产代码里唯一的一份 id 字面量**(0.2.0 已发布契约);穷尽 match:加一支就编不过。 + /// 这是**生产代码里 id 的源头表**(0.2.0 已发布契约):别处要么委派到这里,要么是逐字 + /// 副本(`ull help instances` 的词表行,由 CLI 的 `help_lists_every_stable_id` 守着)。 + /// 穷尽 match:加一支就编不过。 #[must_use] pub const fn as_str(self) -> &'static str { match self { diff --git a/crates/presenter/src/dto/instances.rs b/crates/presenter/src/dto/instances.rs index 341ed14..daf606d 100644 --- a/crates/presenter/src/dto/instances.rs +++ b/crates/presenter/src/dto/instances.rs @@ -76,8 +76,8 @@ impl LoaderKind { /// 稳定 id(`vanilla` / `fabric` / …;GUI 与 `--json` 同词表)。 /// - /// **委派**给 [`CoreLoaderKind::as_str`](M126):整张词表在 core 只有一份字面量 - /// (0.2.0 已发布契约),这里只剩一次结构映射。 + /// **委派**给 [`CoreLoaderKind::as_str`](M126):词表的源头表在 core(0.2.0 已发布契约; + /// 另一处逐字副本是 `ull help instances` 的词表行),这里只剩一次结构映射。 #[must_use] pub fn as_str(self) -> &'static str { self.to_core().as_str() @@ -282,7 +282,8 @@ mod tests { } /// 对外契约:七个 id 在测试里**独立抄一遍**(0.2.0 已发布形状),并与 core 的 `ALL` 做 - /// **集合比较**——这是「加了一支枚举却没人更新对外词表」的唯一守卫。 + /// **集合比较**——这是对外词表的守卫(与 CLI 的 help 守卫互补;显示路径 + /// `presenter::host::game::dto_kind` 目前没有守卫,见 `game::loader` 的模块文档)。 /// /// 为什么不是编译器管:编译期只拦得到「漏 match」,而新种类在三处消费方用最省事的 /// `=> Vanilla` 糊过去**编得过**,那时新加载器会静默显示成原版(`game::loader` 模块文档