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 是**静默跳过**,不是误红)。
107 lines
5.4 KiB
Rust
107 lines
5.4 KiB
Rust
//! 设置页的**分组消息投递口**(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}):\
|
||
两个组共用一个消息类型,四条线又会撞同一个追加锚点"
|
||
);
|
||
}
|
||
}
|
||
}
|