复审 r2 两条 p2,都修了,并按复审给的口径验过红。
**P2-1(续):不兼容档不再只有裸 id**
`FailureKind::Incompatible` 的项目**确实存在**(站点只是没有匹配目标版本/加载器的条目),
所以失败路径上值得现取一次名字——`plan::names_for_incompatible` 按失败项去重、一项一次 GET
(`Modrinth::project`),`fetch_title` 把任何失败都折成 `None`:
**拿名字失败不许影响安装结果**,取不到 / 拿到空标题就退回 id。只有这一档发请求;
「取不到」档 404 那部分项目根本不存在,再发一次只会再吃 404。
**P2-2(新):重试轮把根的标题贴到了前置头上**
根因是判「这条是不是根项目自己失败」用了 `plan.request.roots.first()`,而重试那一轮的 plan
来自 `retry_request()`,它的 `roots` 就是上一轮的失败名单——第一个是前置。
现在判据改成 **id 比对**(`Root{id,title}`,id 由 `remember` 一起记下来):
`label_of` 只在这条失败的 id 真的等于原始根 id 时才用根的标题。复审给的那个形状
(roots=["P7dR8mSH","1eAoo2KR"] + root_title="Sodium")现在画 `P7dR8mSH`,不再是
`Sodium (P7dR8mSH)`。
**守门(每条都真跑红过,且在拆完模块的最终代码上复跑)**
- 去掉不兼容档的名字来源 ⇒ `an_incompatible_item_shows_the_project_name_once_it_is_known` 红
(`left: "P7dR8mSH"` / `right: "Entity Culling (P7dR8mSH)"`)。
- `label_of` 换回 `roots.first()` ⇒ `the_retry_round_does_not_paste_the_root_title_onto_a_dependency` 红
(`left: ["Sodium (P7dR8mSH)","1eAoo2KR"]`,正是复审实测的那一行)。
- 取名字不降级(`expect`)⇒ `a_failed_name_lookup_falls_back_to_the_id_without_breaking_anything` 红。
**行数**:`plan.rs` 加完这套到 414 行、`install.rs` 到 403 行,都越过 400 硬线 ⇒
把「名字从哪来」整块(`Root`/`Names`/`names_for_incompatible`/`label_of`/`fetch_title`)
纯搬移到 `plan/labels.rs`(111 行):`plan.rs` 336、`install.rs` 388。
107 lines
4.4 KiB
Rust
107 lines
4.4 KiB
Rust
//! 「上一次装入留下的计划」表(M191):`retry_plan()` 的输入。
|
||
//!
|
||
//! 从 `plan.rs` 拆出(父文件加了 `label_of` 之后顶到 429 行,越过 400 行硬线)。
|
||
//! 拆法与本仓既有 `-p1/-p2` 一致:**纯搬移**,一行不改语义;调用方路径不变
|
||
//! (`plan.rs` 仍然 `pub(super) use` 这三个函数)。
|
||
|
||
use std::path::{Path, PathBuf};
|
||
use std::sync::Mutex;
|
||
|
||
use unlucky_core::addon::online::DependencyPlan;
|
||
|
||
use super::super::super::env::HostEnv;
|
||
|
||
/// 「上一次装入留下的计划」表的一行(M191):`只重试这些` 的输入。
|
||
///
|
||
/// ## 为什么要一张静态表(说清楚这笔债)
|
||
///
|
||
/// 重试要的输入是**上一轮那份 `DependencyPlan`**(`retry_request()` 是它的方法)。它现在活在
|
||
/// `CoreHost`(`host/dispatch.rs`)之外,因为那个文件不在本 mission 的 scope;与
|
||
/// `addon/pack.rs` 的 `PACK_STATE` 同一手法:每 host 一格(键 = 数据根),于是测试里两个 host
|
||
/// 互不干扰。`host/mod.rs` 一旦进 scope,把这张表搬成一个字段即可(这表就是那个字段)。
|
||
///
|
||
/// 键里带 `instance`:同一台机器上往两个实例装过东西,重试只该重试这个实例那一次的计划。
|
||
/// 成功之后由 [`forget`] 清掉——「全成功」时不该留下任何可重试的东西。
|
||
///
|
||
/// ## 为什么连**原始根项目 id** 一起记(r2 复审 P2-2)
|
||
///
|
||
/// 「这一条是不是根项目自己失败」不能靠 `plan.request.roots.first()` 判:重试那一轮的 plan 来自
|
||
/// `DependencyPlan::retry_request()`,它的 `roots` 就是**上一轮的失败名单**——第一个是前置,
|
||
/// 拿它跟根的显示名一比,就会把 `Sodium` 贴到一个前置头上。所以判据只能是「这个 id 是不是
|
||
/// 当初那次装入的根 id」,那个 id 得跟计划一起记下来。
|
||
struct Entry {
|
||
host: PathBuf,
|
||
instance: String,
|
||
/// 落盘目录(重试要按它重新扫 `present`,且**不许**换目录)
|
||
dir: PathBuf,
|
||
/// 当初那次装入的根项目 id(`ContentInstallRequest.project`,判「是不是根」的唯一依据)
|
||
root: String,
|
||
/// 当初那次装入的显示名(只配给 `root` 用)
|
||
title: String,
|
||
plan: DependencyPlan,
|
||
}
|
||
|
||
static REMEMBERED: Mutex<Vec<Entry>> = Mutex::new(Vec::new());
|
||
|
||
/// 取回的那一份记录(拆成结构体而不是四元组:调用点读得懂哪一个是哪个)。
|
||
pub(in crate::host::addon) struct Remembered {
|
||
/// 落盘目录
|
||
pub dir: PathBuf,
|
||
/// 当初那次装入的根项目 id
|
||
pub root: String,
|
||
/// 当初那次装入的显示名
|
||
pub title: String,
|
||
/// 上一轮的依赖计划(`retry_request()` 的来源)
|
||
pub plan: DependencyPlan,
|
||
}
|
||
|
||
/// 记下这一轮的计划(**覆盖**同一个 host + 实例的上一份)。
|
||
pub(in crate::host::addon) fn remember(
|
||
env: &HostEnv,
|
||
instance: &str,
|
||
dir: &Path,
|
||
root: &str,
|
||
title: &str,
|
||
plan: &DependencyPlan,
|
||
) {
|
||
let host = env.layout.home().to_path_buf();
|
||
let mut table = REMEMBERED
|
||
.lock()
|
||
.unwrap_or_else(|poison| poison.into_inner());
|
||
table.retain(|item| !(item.host == host && item.instance == instance));
|
||
table.push(Entry {
|
||
host,
|
||
instance: instance.to_string(),
|
||
dir: dir.to_path_buf(),
|
||
root: root.to_string(),
|
||
title: title.to_string(),
|
||
plan: plan.clone(),
|
||
});
|
||
}
|
||
|
||
/// 忘掉这个实例的计划(装成功 / 没有失败项时调:没有可重试的东西了)。
|
||
pub(in crate::host::addon) fn forget(env: &HostEnv, instance: &str) {
|
||
let host = env.layout.home().to_path_buf();
|
||
let mut table = REMEMBERED
|
||
.lock()
|
||
.unwrap_or_else(|poison| poison.into_inner());
|
||
table.retain(|item| !(item.host == host && item.instance == instance));
|
||
}
|
||
|
||
/// 这个实例上一次失败的计划(+ 落盘目录 + 原始根 id 与显示名);`None` = 没有可重试的记录。
|
||
pub(in crate::host::addon) fn remembered(env: &HostEnv, instance: &str) -> Option<Remembered> {
|
||
let host = env.layout.home().to_path_buf();
|
||
let table = REMEMBERED
|
||
.lock()
|
||
.unwrap_or_else(|poison| poison.into_inner());
|
||
table
|
||
.iter()
|
||
.find(|item| item.host == host && item.instance == instance)
|
||
.map(|item| Remembered {
|
||
dir: item.dir.clone(),
|
||
root: item.root.clone(),
|
||
title: item.title.clone(),
|
||
plan: item.plan.clone(),
|
||
})
|
||
}
|