- P2-2:keys.rs 的 schema 版本史按原文逐字还原(1.4.0/1.5.0 的键名、1.2.0 的迁移口径、 旧读新的完整结论),不再用版本文档换行数;腾行改从我自己新加的注释里出。 - P2-1:download.retry_limit 范围恢复 0~10,并把 0 的语义定成「只试一次」(attempts = max(1, 0)): 老配置合法、零迁移、ull config get 读到的 0 与引擎真做的事一致(不再回落出厂 3)。 schema 描述与 presenter/iced 两处 UI 都写清「0 与 1 都 = 只试一次,不代表不下载」。 - P2-3:shared.rs 的「唯一一处装配」更正为「presenter 侧唯一一处」(CLI 还有三处自建), docs 加「哪些路径吃这套设置」表,并立 finding(CLI install/java/pack 不读设置、并发 64→8)。 - P3-3:mirrors.rs 的 gh_proxies 注释改成「无生产读者,仅解析留档」。 - P3-4(presenter 侧):并发/重试描述的出厂值与上限改用常量 format!,不再是第二处字面量。 - 出图重拍(含 0/1 = 只试一次)。
179 lines
7.9 KiB
Rust
179 lines
7.9 KiB
Rust
//! 设置页「下载」分组 + 下载调优的**宿主侧翻译**(M193)。
|
||
//!
|
||
//! ## 为什么单开一个文件
|
||
//!
|
||
//! 同 `host/settings_tests.rs` 的理由:`host/settings.rs` 正好顶在 400 行硬线上,而本轮要往这一组加
|
||
//! 「`download.gh_proxy` 三态行 + 并发硬上限说明 + 调优翻译」约 24 行。不拆就得砍 `settings.rs` 里
|
||
//! 那些**留着防复发**的论证注释(`ai_group` 的 M174 回归说明、`parse_value` 的 P1-1 浮点键事故)
|
||
//! ——那比多一个文件糟得多。搬出来还有一个附带好处:这一组是**唯一**要往 core 灌下载调优的一组,
|
||
//! 代码与它的守卫(`settings_tests.rs`)现在挨在一起。
|
||
//!
|
||
//! ## 谁在读 / 谁在写
|
||
//!
|
||
//! ```text
|
||
//! config/launcher.json ←── store.set(`settings::set`,本模块只读)
|
||
//! download.concurrency ─┐
|
||
//! download.retry_limit ─┼─→ DownloadTuning(core 决定默认值与夹取)─→ shared::set_tuning
|
||
//! download.gh_proxy ───┘ (引擎重建时生效)
|
||
//! ```
|
||
//!
|
||
//! **调优的同步点是显式的**:`host/engine.rs` 每次取引擎前、`dispatch.rs` 的 `SetSetting` 分支里,
|
||
//! 都调 [`apply_tuning`](幂等:值没变就什么都不做)。它**不是**藏在某个读函数里的副作用——
|
||
//! 「读起来是纯读、实际改全局状态」正是本仓吃过亏的那类形状。
|
||
|
||
use unlucky_core::download::config::{
|
||
DownloadTuning, DEFAULT_MAX_ATTEMPTS_PER_SOURCE, DEFAULT_MAX_CONCURRENCY, MAX_CONCURRENCY,
|
||
};
|
||
use unlucky_core::download::shared;
|
||
|
||
use crate::dto::{SettingGroupView, SettingRowView};
|
||
|
||
use super::super::env::HostEnv;
|
||
use super::{int, selected, text};
|
||
|
||
/// 并发数的推荐档位(`selected()` 找不到时回 0;手改出来的非档位值会被插进选项表,见 [`group`])。
|
||
const CONCURRENCY_LADDER: [&str; 5] = ["4", "8", "16", "32", "64"];
|
||
/// 单源尝试次数的推荐档位(`0` 是旧版 GUI 提供过的档位,语义 = 只试一次,见 schema 描述)。
|
||
const RETRY_LADDER: [&str; 6] = ["0", "1", "2", "3", "5", "10"];
|
||
|
||
/// 下载(D7):并发/重试/地区/镜像偏好/代理/GitHub 加速前缀。
|
||
pub(super) fn group(env: &HostEnv) -> SettingGroupView {
|
||
let concurrency = int(
|
||
env,
|
||
unlucky_core::download::config::KEY_CONCURRENCY,
|
||
DEFAULT_MAX_CONCURRENCY as i64,
|
||
)
|
||
.to_string();
|
||
let retry = int(
|
||
env,
|
||
unlucky_core::download::config::KEY_RETRY_LIMIT,
|
||
i64::from(DEFAULT_MAX_ATTEMPTS_PER_SOURCE),
|
||
)
|
||
.to_string();
|
||
let concurrency_options = ladder(&CONCURRENCY_LADDER, &concurrency);
|
||
let retry_options = ladder(&RETRY_LADDER, &retry);
|
||
let concurrency_options: Vec<&str> = concurrency_options.iter().map(String::as_str).collect();
|
||
let retry_options: Vec<&str> = retry_options.iter().map(String::as_str).collect();
|
||
let regions = ["auto", "cn", "global"];
|
||
let mirrors = ["auto", "official", "mirror"];
|
||
let proxies = ["none", "system", "manual"];
|
||
let region_value = text(env, "download.region");
|
||
let mirror_value = text(env, "download.mirror_preference");
|
||
let proxy_value = text(env, "download.proxy_mode");
|
||
SettingGroupView::new("download", "下载与镜像(D7)")
|
||
.with(SettingRowView::choice(
|
||
unlucky_core::download::config::KEY_CONCURRENCY,
|
||
"并发数",
|
||
&format!(
|
||
"同时下载的文件数:出厂 {DEFAULT_MAX_CONCURRENCY},可调 1~{MAX_CONCURRENCY}(引擎硬上限,防止填 9999 把机器拖死);越界或手改坏值按出厂值生效"
|
||
),
|
||
&concurrency_options,
|
||
selected(&concurrency_options, &concurrency),
|
||
))
|
||
.with(SettingRowView::choice(
|
||
unlucky_core::download::config::KEY_RETRY_LIMIT,
|
||
"单源尝试次数",
|
||
&format!(
|
||
"同一个源最多试几次(含首次;出厂 {DEFAULT_MAX_ATTEMPTS_PER_SOURCE})。0 与 1 都等于「只试一次、不重试」——0 是旧版提供的档位,不代表「不下载」。换源不受此限"
|
||
),
|
||
&retry_options,
|
||
selected(&retry_options, &retry),
|
||
))
|
||
.with(SettingRowView::choice(
|
||
"download.region",
|
||
"地区判定",
|
||
"auto = 时区+语言+探测;决定是否优先国内镜像",
|
||
®ions,
|
||
selected(
|
||
®ions,
|
||
if region_value.is_empty() {
|
||
"auto"
|
||
} else {
|
||
®ion_value
|
||
},
|
||
),
|
||
))
|
||
.with(SettingRowView::choice(
|
||
"download.mirror_preference",
|
||
"镜像偏好",
|
||
"auto = 按健康度评分;official = 官方源优先",
|
||
&mirrors,
|
||
selected(
|
||
&mirrors,
|
||
if mirror_value.is_empty() {
|
||
"auto"
|
||
} else {
|
||
&mirror_value
|
||
},
|
||
),
|
||
))
|
||
.with(SettingRowView::choice(
|
||
"download.proxy_mode",
|
||
"代理模式",
|
||
"none / system(跟随系统)/ manual(下面填地址)",
|
||
&proxies,
|
||
selected(
|
||
&proxies,
|
||
if proxy_value.is_empty() {
|
||
"none"
|
||
} else {
|
||
&proxy_value
|
||
},
|
||
),
|
||
))
|
||
.with(SettingRowView::text(
|
||
"download.proxy_url",
|
||
"HTTP 代理",
|
||
"manual 模式生效;留空则用系统代理",
|
||
&text(env, "download.proxy_url"),
|
||
))
|
||
// M193:gh-proxy 三态。**留空与 off 是两件事**,描述里必须都写出来——用户最容易把
|
||
// 「我还没决定」当成「我不要加速」,于是以为自己关掉了、实际还在套前缀。
|
||
.with(SettingRowView::text(
|
||
unlucky_core::download::config::KEY_GH_PROXY,
|
||
"GitHub 加速前缀(gh-proxy)",
|
||
"留空 = 跟随内置默认(镜像优先/国内判定时自动套 gh-proxy.com);填 off = 关闭(不生成任何 gh-proxy 候选);填一个 URL 前缀 = 覆盖它",
|
||
&text(env, unlucky_core::download::config::KEY_GH_PROXY),
|
||
))
|
||
.with(SettingRowView::action(
|
||
super::super::engine::DETECT_REGION_ACTION,
|
||
"重新检测地区",
|
||
"清掉 24h 地区缓存,下次下载重新判定",
|
||
"detect_region",
|
||
))
|
||
}
|
||
|
||
/// 推荐档位 + 「当前值不在档位里就把它插进去」(否则这一行会把 2 显示成 4 —— 又一个假值)。
|
||
fn ladder(ladder: &[&str], current: &str) -> Vec<String> {
|
||
let mut options: Vec<String> = ladder.iter().map(|item| (*item).to_string()).collect();
|
||
if !current.is_empty() && !options.iter().any(|option| option == current) {
|
||
options.push(current.to_string());
|
||
options.sort_by_key(|option| option.parse::<i64>().unwrap_or(i64::MAX));
|
||
}
|
||
options
|
||
}
|
||
|
||
/// 从 `launcher.json` 读出下载调优(默认值与夹取口径在 core,本函数只做「取原始值」)。
|
||
#[must_use]
|
||
pub(crate) fn tuning_of(env: &HostEnv) -> DownloadTuning {
|
||
use unlucky_core::download::config::{KEY_CONCURRENCY, KEY_GH_PROXY, KEY_RETRY_LIMIT};
|
||
let store = env.store();
|
||
let doc = store.doc();
|
||
let gh = doc
|
||
.effective_str(KEY_GH_PROXY)
|
||
.map(str::to_string)
|
||
.unwrap_or_default();
|
||
DownloadTuning::from_settings(
|
||
doc.effective_i64(KEY_CONCURRENCY),
|
||
doc.effective_i64(KEY_RETRY_LIMIT),
|
||
Some(gh.as_str()),
|
||
)
|
||
}
|
||
|
||
/// 把当前设置灌进 core 的下载调优(按数据根),返回**是否真的变了**(变了就失效该根的引擎)。
|
||
///
|
||
/// 幂等:值没变时 `shared::set_tuning` 一行不动,所以「每次取引擎前都调」是安全的。
|
||
pub(crate) fn apply_tuning(env: &HostEnv) -> bool {
|
||
shared::set_tuning(env.layout.cache(), tuning_of(env))
|
||
}
|