M205: gh-proxy 键绑定断言、外层重试预算判据、UA 版本号来源、下载设置文档更正
- setting/keys/proxy: 新增断言把 KEY_GH_PROXY 常量与 schema 登记绑起来(键名 / Text{max_len:256} / 默认空串)
- addon/online/download: RETRY_ATTEMPTS 与引擎每源次数是两件事(层级相乘、管的失败类型不同),写清判据并用两条编译期断言钉住 3 与 3x3=9 的边界
- presenter/host/addon/http: UA 版本号改为从 CARGO_PKG_VERSION 拼出,补断言钉住版本 == CARGO_PKG_VERSION 且联系方式段不丢
- docs/download-settings: §3.1 四条 CLI 命令都吃设置(update 是自更新路径),§6 补 M11-M13 三条 CLI 变异,§1/§7 里 gh_proxy 已合入的事实
This commit is contained in:
1 parent
4c29cbac3e
commit
d4aa3b23f4
4 files changed
+119
-16
No files matched your search
@@ -25,14 +25,45 @@ use std::path::{Path, PathBuf};
|
||||
use sha1::Digest as _;
|
||||
use sha2::Sha512;
|
||||
|
||||
use crate::download::config::DEFAULT_MAX_ATTEMPTS_PER_SOURCE;
|
||||
use crate::download::{DownloadEngine, DownloadRequest, Integrity, ResourceKind};
|
||||
|
||||
use super::modrinth::HashAlgorithm;
|
||||
use super::transport::{OnlineError, OnlineErrorKind, OnlineFuture};
|
||||
|
||||
/// 最多尝试几次(含首次)。3 次 = 镜像/健康度换源之外还给一次真重试。
|
||||
/// 本模块外层「整条下载」的预算(含首次):最多取几遍文件。
|
||||
///
|
||||
/// # 它**不是**引擎的每源尝试次数,所以不引用 `download::config` 的常量(M205 判定)
|
||||
///
|
||||
/// 数值恰好都是 3,但数的不是同一件事。三条判据,任一条都足够说明「合并成一处」会把语义拍平:
|
||||
///
|
||||
/// 1. **层级不同,而且是相乘**:引擎那层在 [`ArtifactFetcher`] 里面,按**源**数(镜像展开后每个候选
|
||||
/// 各有一份预算,见 `DownloadEngine::attempts_for`;源在冷却期时还会缩成 1 次恢复探测)。本模块这
|
||||
/// 层只数「取一遍 + 复核一遍」,每数一次都要重新走一遍引擎 —— 最坏情况下的真实请求数 ≈
|
||||
/// [`RETRY_ATTEMPTS`] × 引擎每源预算,合并成一个数等于把最坏请求数变成平方级。
|
||||
/// 2. **管的失败类型不同**:sha512 不符与字节数不符只有本模块知道(引擎只认 sha1,`Integrity` 没有
|
||||
/// Sha512 档),这类失败引擎**不会**重试,必须由这一层重取 —— 这一层的预算里有一档是引擎看不见的。
|
||||
/// 3. **改它的性质不同**:`download.retry_limit` 是用户可见的设置(0~10、进 JSON schema,也就是说引擎
|
||||
/// 那个默认值会因为**用户的**配置而变);这里的 3 是内容库元数据请求的产品口径,没有设置键,也不该
|
||||
/// 被某个设置项的出厂默认牵着走。
|
||||
///
|
||||
/// **边界**:最坏情况 = [`RETRY_ATTEMPTS`](外层)× [`DEFAULT_MAX_ATTEMPTS_PER_SOURCE`](引擎每个源
|
||||
/// 一次请求内的尝试)= 9 次真实网络请求。下面两条注解级断言(编译期求值)钉住这个乘积:任一侧想改,
|
||||
/// 都得先读到这段判据,而不是被一次「统一常量」的顺带改动悄悄翻倍。
|
||||
pub const RETRY_ATTEMPTS: u32 = 3;
|
||||
|
||||
/// 外层预算被改了吗(改之前先读 [`RETRY_ATTEMPTS`] 的 doc:它不是引擎的每源次数)。
|
||||
const _: () = assert!(
|
||||
RETRY_ATTEMPTS == 3,
|
||||
"外层下载预算变了:它不是 download::config 的每源尝试次数,改它要单独一个决定"
|
||||
);
|
||||
|
||||
/// 最坏情况下的真实请求数(3 × 3)——外层与引擎每源这两个「3」不共享来源,乘积才是事实。
|
||||
const _: () = assert!(
|
||||
RETRY_ATTEMPTS as u64 * DEFAULT_MAX_ATTEMPTS_PER_SOURCE as u64 == 9,
|
||||
"最坏真实请求数变了(外层 × 引擎每源):先读 RETRY_ATTEMPTS 的 doc 再改"
|
||||
);
|
||||
|
||||
/// 一次下载请求(只含「引擎需要什么」,不含任何站点字段)。
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FetchArgs<'a> {
|
||||
|
||||
@@ -74,4 +74,23 @@ mod tests {
|
||||
assert!(gh.doc.contains("留空 = 跟随内置默认"), "{}", gh.doc);
|
||||
assert!(gh.doc.contains("填 off 关闭"), "{}", gh.doc);
|
||||
}
|
||||
|
||||
/// **`KEY_GH_PROXY` 与 schema 登记的绑定**(M205):`download/config.rs` 那个常量是宿主读数用的
|
||||
/// 唯一键名,本文件 `specs()` 里是 schema 的登记——两侧各写一个字符串,此前没有任何断言把它们
|
||||
/// 绑在一起,常量被改名/写错时全绿,行为只在宿主侧静默退化成 `Builtin`(用户填的 `off` 被忽略)。
|
||||
///
|
||||
/// 抓得住:常量值写错(如 `download.ghproxy`)⇒ 找不到登记项,第一句红;登记的 `max_len` 从 256
|
||||
/// 改掉、或默认值从空串改成别的 ⇒ 各自红(类型/默认值是宿主的读侧契约)。
|
||||
/// 抓不住:两侧**同时**改名(那是一次公开改名,本用例判断不了它对不对,只能保证两侧一致)。
|
||||
#[test]
|
||||
fn the_gh_proxy_key_is_bound_to_the_schema_registration() {
|
||||
use crate::download::config::KEY_GH_PROXY;
|
||||
use crate::setting::spec::ValueKind;
|
||||
|
||||
let Some(gh) = specs().into_iter().find(|spec| spec.key == KEY_GH_PROXY) else {
|
||||
panic!("下载键常量 {KEY_GH_PROXY:?} 在 schema 里没有登记项(常量值与 specs() 的键名不一致)");
|
||||
};
|
||||
assert_eq!(gh.kind, ValueKind::Text { max_len: 256 }, "{}", gh.key);
|
||||
assert_eq!(gh.default, serde_json::json!(""), "{}", gh.key);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user