fix(java): 把「这一页不存在」与「这一页读不懂」分开,解析失败不再被当成翻到尾巴(M280 r3)
r3 复审 p2-1items:r2 的吞判据按 `JavaErrorKind` 判,而 `Metadata` 这个桶里除了
404/410 与 200+空体,还装着**解析失败**(`parse_release_list`)⇒ 第二页起上游回
「看得懂的状态码 + 读不懂的内容」时,清单被静默截断且标成 `Complete`,用户看到的是
「这个 major 没有版本 X(可用:…)」,而 X 其实在上游。
## 修法:语义信号代替错误种类(复审判据里没选字面方案,选了改动面最小的等价实现)
`fetch_page` 的返回类型变成 `Result<Option<Vec<String>>, JavaError>`,`None` 只由
**新的纯函数 `page_outcome(status, body)`** 产生:404/410 或 200+空体。判据是状态码 +
体形状,**不匹配任何错误文案**。
- `page > 0` 的 `None` ⇒ 翻到尾巴:交回已取到的全部,`truncated = false`(r2 已做对,保留);
- `page == 0` 的 `None` ⇒ 这个大版本真没有 GA 清单:仍然 `Err`(与 r2 之前同口径,
只是判据从「错误种类」换成「哪一页不存在」);
- **任何页的解析失败 / 非 2xx 非 404 / 网络错误 ⇒ 一律 `Err`**(不再有「读不懂 ⇒ 没有更多」);
- 错误落在最后一页也是 `Err`,不可能再报 `Complete`。
`api/version.rs` 里新增 `page_outcome`(纯)+ `fetch_release_page`(HTTP 那一跳,判定全交给前者),
`fetch_version` 的闭包改调 `fetch_release_page`。只动本 mission scope 内的两个文件,
没有改 `net.rs`,也没有给跨 crate 匹配的 `JavaErrorKind` 加变体(cli / app-iced 的 match 不变)。
## 用例(离线;每条都有能变红的变异)
- `a_missing_page_after_a_full_page_ends_the_tail_cleanly`(判据 1):page0=20 条、page1=None
⇒ `Ok((20, false))`,逐条相等、`calls == 2`;
- `a_missing_page_is_404_410_or_an_empty_body_and_nothing_else`:404/410/200+空白 ⇒ `None`;
正常 JSON ⇒ `Some(names)`;`<html>502</html>` ⇒ `Err(Metadata)`;500/403/429/502 ⇒ `Err(Network)`;
- `a_parse_failure_on_a_later_page_is_an_error_not_the_end_of_the_list`(判据 2):夹具用**真的**
`parse_release_list("<html>502</html>").unwrap_err()` ⇒ 必须 `Err`,且报的是解析失败本身;
`Err` 分支在签名上就带不出部分结果;
- `an_error_on_the_last_allowed_page_is_never_reported_as_complete`(判据 3):错误落在
`page == MAX_PAGES-1` ⇒ 必须 `Err`,不许 `Ok((_, false))`;
- `a_missing_first_page_is_still_an_error`:第一页就不存在 ⇒ `Err`;
- `a_network_error_on_a_later_page_is_not_swallowed`:网络错误照抛。
变异(做完都还原,`git status` 干净):R3-A 把 page>0 的 None 当失败 ⇒ 判据 1 那条红;
R3-B 把 Metadata 重新并进吞掉的分支 ⇒ 判据 2/3 两条红(panic 原文就是 `(…20 条…, false)`,
即复审实测的那个 bug);R3-C 把 `page_outcome` 的解析失败并成 `None` ⇒ 底座那条红。
## 顺带两条(复审 P3)
- P3-1:报告里「钉住上游越界页就是 404」那句改成正确表述(夹具手写,钉的是「收到『这一页
不存在』要把已取到的交出去」);`--lib` 数字按 tip 重数(`aac0eef` 上是 880,本轮 +3 ⇒ 883)。
- P3-2:删掉 `catalog/tests.rs` 里冗余的否定式断言(紧邻的正向断言已蕴含它),
保留两条正向断言(`Adoptium 里可用` / `Oracle 镜像目录里可用`)。
门禁(CARGO_TARGET_DIR=C:/Temp/ull-target/wt-280):core --lib 883 / --test game 120 /
--test launch 91、cli --all-targets、fmt --check、clippy -D warnings 全部 EXIT=0。