fix(addon): 复审 r1 —— 失败去重、null 载荷、完成态进度、顺序守门、下载失败归并
- P2-1 失败分支不再「直接 continue」:`fail()` 记一条失败(`record_failure` 按 `requested_as` 去重)并把请求键写进 `alias`,后续同键走现成的 `fold_parents` 折父边 ⇒ 同一项目只取一次版本、失败只一条、重试名单不重复; - P2-2 `RawDependency.dependency_type` 改 `Option<String>`:`#[serde(default)]` 兜不住 `null`,以前一份 `"dependency_type": null` 就让整份 `/version` 报 Malformed、整个项目装不上; 现在 null/未知一律 `Unknown`(不递归); - P2-3 完成态用 `finish()` 把 `files_done` 对齐 `files_total`(曾写死 `1`,装 3 个停在 1/3); 解析前 `files_total` 归 0(未知态),不再假装「已知 1 个文件」; - P2-4 新增 `tests/order.rs`:跨根的层内顺序 + 多根正序/逆序对照,删 `layer.sort_by` 会红; 另加 `required_project_ids` 自己的排序去重契约用例(`tests/regressions.rs`); - P2-5 下载阶段失败记进同一套两档结构(`Unreachable` + 叶子成因)并继续下一个文件: 已下好的不回滚、失败项进可重试名单、文案保留「已装入 N 个」的上下文;`retry_request` 自动覆盖它们; - nit① 在 `plan.rs` 写清「不兼容」为什么落在 DTO 的 `Unsupported`(不是笔误,是缺档); nit② 新增 `DependencyPlan::unresolved_required`(只有版本 id 的必备前置不再静默忽略); nit③ 补「同 mod 另一个版本在目录里仍要下载」的负向用例。
This commit is contained in:
1 parent
011d525e8d
commit
aa33210bb6
12 files changed
+686
-77
No files matched your search
@@ -220,6 +220,20 @@ pub struct DependencyFailure {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
/// 声明了 `required`、但**只给了 `version_id`**(没有 `project_id`)的依赖。
|
||||
///
|
||||
/// BFS 需要项目 id 才能取版本(`/v2/project/{id}/version`),所以这条既装不了、也不该被
|
||||
/// 假装没看见:留在计划里,上层才能说「有 N 条前置没法解析」而不是默默少装一个。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UnresolvedRequired {
|
||||
/// 声明它的项目(规范 id)
|
||||
pub parent: String,
|
||||
/// 站点给的版本 id(`None` = 连版本 id 都没有,只有文件名)
|
||||
pub version_id: Option<String>,
|
||||
/// 站点给的文件名
|
||||
pub file_name: Option<String>,
|
||||
}
|
||||
|
||||
/// 一次 BFS 的完整结果(成功、跳过、失败三份名单,都**不丢**)。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DependencyPlan {
|
||||
@@ -233,6 +247,8 @@ pub struct DependencyPlan {
|
||||
pub failures: Vec<DependencyFailure>,
|
||||
/// 撞到 `max_depth` 而没展开的项目键(排序去重)
|
||||
pub truncated: Vec<String>,
|
||||
/// 有 `required` 但没有项目 id、解析不了的前置(排序去重)
|
||||
pub unresolved_required: Vec<UnresolvedRequired>,
|
||||
}
|
||||
|
||||
impl DependencyPlan {
|
||||
@@ -245,6 +261,7 @@ impl DependencyPlan {
|
||||
skipped: Vec::new(),
|
||||
failures: Vec::new(),
|
||||
truncated: Vec::new(),
|
||||
unresolved_required: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,19 +276,45 @@ impl DependencyPlan {
|
||||
self.failures.iter().filter(move |item| item.kind == kind)
|
||||
}
|
||||
|
||||
/// 记一条失败——**按 `requested_as` 去重**,父边合并。
|
||||
///
|
||||
/// 为什么要有这道去重:同一个项目可能被两条路径要求(A→GONE、B→C→GONE)。若不去重,
|
||||
/// 「可只重试这 N 个」会写出 `GONE、GONE`,用户按名单重试等于发两次一样的请求。
|
||||
/// 解析阶段与下载阶段的失败都走这里,于是名单里一个项目只出现一次。
|
||||
pub fn record_failure(&mut self, failure: DependencyFailure) {
|
||||
if let Some(existing) = self
|
||||
.failures
|
||||
.iter_mut()
|
||||
.find(|item| item.requested_as == failure.requested_as)
|
||||
{
|
||||
existing.required_by.extend(failure.required_by);
|
||||
existing.required_by.sort();
|
||||
existing.required_by.dedup();
|
||||
// 深度取更浅的那条:越靠近根越该先修
|
||||
existing.depth = existing.depth.min(failure.depth);
|
||||
return;
|
||||
}
|
||||
self.failures.push(failure);
|
||||
}
|
||||
|
||||
/// 「可重试名单」:失败项原样重启一次解析的请求。
|
||||
///
|
||||
/// 只含失败项 ⇒ 用户只重试这一部分,**不重下**已经装好的文件(`present` 沿用)。
|
||||
/// 两档都在名单里,但每条自带 [`FailureKind`]:不兼容那一档重试前得换版本/加载器,
|
||||
/// 换完再调它才有意义。**这不是「后台自动重试」**——本模块只给请求,什么时候发由上层决定。
|
||||
///
|
||||
/// 调用方如果刚跑完一轮下载,重试前应**重新扫一遍目录**再填 `present`:这一轮新落盘的
|
||||
/// 文件不在旧的 `present` 里,不重扫就会把它们当「还没有」再下一份。
|
||||
#[must_use]
|
||||
pub fn retry_request(&self) -> ResolveRequest {
|
||||
let mut roots: Vec<String> = Vec::new();
|
||||
for failure in &self.failures {
|
||||
if !roots.contains(&failure.requested_as) {
|
||||
roots.push(failure.requested_as.clone());
|
||||
}
|
||||
}
|
||||
ResolveRequest {
|
||||
roots: self
|
||||
.failures
|
||||
.iter()
|
||||
.map(|failure| failure.requested_as.clone())
|
||||
.collect(),
|
||||
roots,
|
||||
..self.request.clone()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user