Files
Unlucky-Launcher/crates/app-iced/src/pages/settings/message.rs
T
AskaEth d3e959e754 docs(app-iced): M101 r4 —— 订正共享点成本数字(3 / 不可行 / 3 处)与三处 p3 措辞
r3 复审:那段「仍然共享」的成本数字与代码事实不符(三条填页线据此取 scope,故按 p1 处理)。
本轮**只改文档/注释**,无产品代码与测试逻辑改动(唯一非注释改动是 t2 断言的 message 字符串)。

- 第 4 条「读页外真值」:`States` 没有 `Default`,加字段会让 4 处 struct literal 全部 E0063
  (`pages/mod.rs:154/177/232` + `app/view.rs:70`)⇒ 把 model 塞进 `States` 是 **3 个共享文件**
  (含 M104 的落点 `app/view.rs`);改推「给 `settings::view` 加 `model` 形参」这个**真·2 个文件**
  的形状。并订正「在 `app/state.rs` 用真值初始化(1 个文件)」——今天没有语言真值源
  (`AppearancePrefs` 无 language 字段,唯一值是 `app/view.rs:128` 硬编码 `Lang::En`)。
- 第 5 条第 ② 条路(改 `Body` 返回 `Task`)**删掉**:`settings::view` → `pages::render` → `App::view`
  一路返回 `Element`(iced 0.14 的 view 亦然,且 `view(&self)` 改不了状态),view 期间产出的 `Task`
  没有出口;真做要 `settings/mod.rs` + `pages/mod.rs` + `app/view.rs` 三处且另造旁路,明确写「不建议」。
  只留第 ① 条(`app/update.rs` 的 `Message::Boot` → `on_boot`,1 个文件)。
- 第 6 条与 `state.rs::demo_groups` 文档:「harness 侧改两处」→ **三处**
  (`pages/mod.rs` 的 `page` / `page_with_appearance` / `app_with_appearance`,行 156/179/234)。
- p3:`message.rs` 里守门路径改成 `app/update/settings/tests.rs`(并点名 `settings.rs` 只是 mod 声明点);
  `appearance_section.rs` 的「绞线」错字改「绊线」;断言 message 的 `Some(appearance::body)` 改成源码
  实际的 `Some(body)`(本地 fn);`app/update/settings/tests.rs` 的「抓不住 #2」改成两种洞分开说
  (`|` 续行放行;花括号形状的多行 pattern 是**静默跳过**,不是误红)。
2026-11-03 23:59:07 +08:00

107 lines
5.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 设置页的**分组消息投递口**(M101 `SETTINGSMSG`):四条填页线各写各的变体,不再抢
//! `pages/mod.rs` 的枚举尾部与 `app/update/page_messages.rs` 那条不许兜底的 match。
//!
//! ## 这一层为什么存在
//!
//! M100 把设置页的**文件**按分区拆开了,但四条线真正的交汇处仍是同一处追加锚点:
//! `pages::Message` 的枚举尾部 + `page_messages.rs` 的 match 臂(该 match 有
//! `the_page_match_has_no_wildcard_fallback` 守门,不许 `_ =>`)。四个变体、四条线、两个共享
//! 文件 ⇒ 四路并行必然四路冲突。M101 只做一件事:把「加消息」从共享文件搬进各组自己的文件——
//! `pages::Message` 从此只有**这一个**设置页变体。
//!
//! ```text
//! pages::Message::Settings(GroupMessage)
//! ├─ Java(java::Message) → settings/java.rs
//! ├─ Download(download::Message) → settings/download.rs
//! ├─ LanguageAppearance(language_appearance::Message) → settings/language_appearance.rs
//! └─ AiAbout(ai_about::Message) → settings/ai_about.rs
//! ```
//!
//! 四个载荷**必须**是四个不同类型、分别定义在四个文件里;否则「两条线往同一个枚举里加变体」的
//! 冲突只是从 `pages/mod.rs` 搬到了这里。`each_group_owns_its_own_message_type` 用 `type_name`
//! 里的模块路径守这一条。
//!
//! ## 加一支设置页消息要改几个文件(M101 r2 之后)
//!
//! **2 个**:你自己那条线的消息口文件(`settings/<线>.rs`)加一个变体,加上你那条落点
//! (`app/update/settings/<线>.rs`)接一条臂——两处都只归你。`pages/mod.rs`、
//! `page_messages.rs`、`settings/message.rs`、`settings/state.rs`、`app/update/settings.rs` 都不用动。
//!
//! ## 穷尽性:抓得住什么 / 抓不住什么
//!
//! - **抓得住**:某条线加了变体却忘了接线 → 编译器在 `app/update/settings.rs` 的 match 上报
//! **E0004**(那里对 [`GroupMessage`] 做穷尽匹配,没有 `_ =>`)。注意 `page_messages.rs`
//! 那条守卫只认 `pages::Message`,**它抓不住组内变体**——把守卫下放到 `settings.rs` 正是本层
//! 的第二个目的,两个 dispatch 各守一层。
//! - **抓不住**:handler 忽略消息(`let _ = message; Task::none()`)或两个 handler 的**身体**被
//! 写成一样——类型对得上就编得过。那要人读,或者等真接业务后由「点了没反应」的用例抓。
//!
//! 类型还顺手挡掉了另一种错:把 `Java(m)` 转给 `on_settings_download` 编不过(载荷类型不同),
//! 所以「接错组」这一半是编译期兜住的,「接了个空壳」那一半不是。
//!
//! ## 测试
//!
//! 本文件的 [`tests::each_group_owns_its_own_message_type`];dispatch 侧的
//! `the_group_dispatch_has_no_catch_all_arm` 在 `app/update/settings/tests.rs`(`app/update/settings.rs`
//! 只是 `#[cfg(test)] mod tests;` 的声明点,别去那里找)。
use super::{ai_about, download, java, language_appearance};
/// 设置页分组消息:**唯一**一个进 `pages::Message` 的投递口。
///
/// 每个变体只归一条线,载荷类型定义在那条线自己的文件里(见模块文档的四个箭头)。
/// 加变体 = 改自己那个文件 + 改 `app/update/settings/<线>.rs`,两处都只归你。
#[derive(Debug, Clone)]
pub enum GroupMessage {
/// Java 分组(`settings/java.rs`)。
Java(java::Message),
/// Downloads 分组(`settings/download.rs`)。
Download(download::Message),
/// 语言 + 外观两组的共同消息口(`settings/language_appearance.rs`)。
LanguageAppearance(language_appearance::Message),
/// AI / About 分组(`settings/ai_about.rs`)。
AiAbout(ai_about::Message),
}
#[cfg(test)]
mod tests {
use super::*;
/// 四条线的载荷**必须**住在四个不同文件里。
///
/// 抓得住:① 为了少建一个文件,把两个组的 `Message` 塞进同一个文件(四条线又会撞同一个
/// 追加锚点,本 mission 的目的当场作废);② 谁把某个分组文件改名后忘了同步(`type_name`
/// 里带模块路径,改名就红)。
/// 抓不住:变体装得对不对(那是 `app/update/settings.rs` 的 E0004 与其守卫的事),也抓不住
/// handler 身体写空/写反(类型对得上就编得过)。
#[test]
fn each_group_owns_its_own_message_type() {
let homes = [
(
std::any::type_name::<java::Message>(),
"settings::java::Message",
),
(
std::any::type_name::<download::Message>(),
"settings::download::Message",
),
(
std::any::type_name::<language_appearance::Message>(),
"settings::language_appearance::Message",
),
(
std::any::type_name::<ai_about::Message>(),
"settings::ai_about::Message",
),
];
for (actual, expected) in homes {
assert!(
actual.ends_with(expected),
"载荷类型 {actual} 不在它自己的文件里(应为 {expected}):\
两个组共用一个消息类型,四条线又会撞同一个追加锚点"
);
}
}
}