refactor(app-iced): M95 r3 把日志页的展示转换从 app/view.rs 拆出来(纯搬移,行为零变化)

`app/view.rs` 已经 392 行(全仓硬上限 400),而日志页的转换还要加判别与测试,先拆。

- `app/view.rs`:392 → **324** 行,只留 `impl App`、`model()` 与实例页的展示转换,
  另加 `mod logs; use logs::logs_model;`(调用点仍是 `logs_model(state).with_status(lang)`,未改)。
- 新增 `app/view/logs.rs`:**80 行**,逐行搬入 `logs_model` / `log_level` / `log_outcome` /
  `diagnosis_of`,实现体一行未动(含 r2 的「先按 `diagnosis.session` 定身份」写法,
  那是下一个 commit 才改的行为)。

等价比证:搬移前后 `cargo test -p unlucky-app-iced -p unlucky-i18n` 都是
**274 + 6 + 23 passed / 0 failed**(lib + no_hardcoded_tokens + i18n),数量与结果一致。
除模块声明与 import 调整外没有任何逻辑改动。
This commit is contained in:
AskaEth committed 2026-11-02 22:26:59 +08:00
1 parent 1bccf89e5c
commit ee4d82e3d4
2 files changed
+85 -73

No files matched your search

+5 -73
View File
@@ -5,9 +5,7 @@
use iced::{Element, Theme};
use unlucky_i18n::Lang;
use unlucky_presenter::dto::{
Diagnosis, InstanceRuntime, InstanceSummary, LaunchStage, LogLevel, LogOutcome,
};
use unlucky_presenter::dto::{InstanceRuntime, InstanceSummary, LaunchStage};
use unlucky_presenter::router::Route;
use unlucky_presenter::state::AppState;
@@ -20,6 +18,10 @@ use super::message::{nav_item_of, Message};
use super::state::{log_first_frame, App};
use super::APP_TITLE;
mod logs;
use logs::logs_model;
impl App {
/// 主题名(iced 只在 `Theme::name()` 变化时重算受主题影响的缓存)。
pub(super) fn theme(&self) -> Theme {
@@ -130,76 +132,6 @@ pub(super) fn model(
}
}
/// 日志页视图模型(M95):presenter 快照 → 日志行 + 诊断结论。
///
/// **页面不读盘**:日志文本只从 `log_lines`(host 尾随 JSONL 后按批次发的事件)来;会话元信息
/// 从 `log_sessions` 里按「运行中的那一场 → 诊断归属的那一场」找,找不到就不画。
///
/// 两处 DTO 侧的**信息丢失**(已报 finding,不在本 scope 修):`dto::Diagnosis` 没有 core 的
/// `severity`(严重度只能按置信度折算,阈值是债)与 `cause.key`(`title`/`hint` 只能当数据画)。
fn logs_model(state: &AppState) -> pages::Logs {
let report = state.diagnosis.as_ref();
let current = state.selected_session.as_deref();
let session = report
.and_then(|report| state.log_sessions.iter().find(|s| s.id == report.session))
.or_else(|| {
state
.log_sessions
.iter()
.find(|s| Some(s.id.as_str()) == current)
});
pages::Logs {
instance: session.map(|s| s.instance.clone()),
when: session.map(|s| s.when.clone()),
outcome: session.map(|s| log_outcome(s.outcome)),
lines: state
.log_lines
.iter()
.map(|line| pages::Line {
text: line.text.clone(),
level: log_level(line.level),
})
.collect(),
findings: report
.map(|report| diagnosis_of(&report.diagnosis))
.into_iter()
.collect(),
..pages::Logs::default()
}
}
/// 日志等级:presenter 枚举 → 页面等级(**结构化出口**,不是认字符串)。
fn log_level(level: LogLevel) -> pages::Level {
match level {
LogLevel::Debug => pages::Level::Debug,
LogLevel::Info => pages::Level::Info,
LogLevel::Warn => pages::Level::Warn,
LogLevel::Error => pages::Level::Error,
}
}
/// 会话结局:presenter 枚举 → 页面结局(文案走 `LaunchOutcome*` key,页面不画 core 的中文串)。
fn log_outcome(outcome: LogOutcome) -> pages::Outcome {
match outcome {
LogOutcome::Ok => pages::Outcome::Ok,
LogOutcome::Crashed => pages::Outcome::Crashed,
LogOutcome::SilentFail => pages::Outcome::SilentFail,
}
}
/// 一条诊断结论 → 页面结论(严重度折算见 [`logs_model`] 的文档)。
fn diagnosis_of(diagnosis: &Diagnosis) -> pages::Finding {
pages::Finding {
rule: diagnosis.rule.clone(),
severity: pages::Severity::of_confidence(diagnosis.confidence),
title: diagnosis.title.clone(),
hint: diagnosis.hint.clone(),
confidence: diagnosis.confidence,
action: diagnosis.fix.clone(),
}
}
/// 列表行的副文案:`1.20.4 · Fabric 0.16.5`(原版只写「原版」,见 `LoaderInfo::badge`)。
fn supporting_line(instance: &InstanceSummary) -> String {
format!("{} · {}", instance.mc_version, instance.loader.badge())
+80
View File
@@ -0,0 +1,80 @@
//! 日志页的展示转换:presenter 快照 → [`pages::Logs`]。
//!
//! 从 `app/view.rs` 拆出来(M95 r3):那个文件已经 392 行,贴着全仓 400 行的硬上限,
//! 而这一页的转换还要再加判别与测试。**本次搬运逐行等价**,行为零变化;
//! 跨会话串场的修复在下一个 commit(`state.diagnosis` 只写不清那条)。
use unlucky_presenter::dto::{Diagnosis, LogLevel, LogOutcome};
use unlucky_presenter::state::AppState;
use crate::pages;
/// 日志页视图模型(M95):presenter 快照 → 日志行 + 诊断结论。
///
/// **页面不读盘**:日志文本只从 `log_lines`(host 尾随 JSONL 后按批次发的事件)来;会话元信息
/// 从 `log_sessions` 里按「运行中的那一场 → 诊断归属的那一场」找,找不到就不画。
///
/// 两处 DTO 侧的**信息丢失**(已报 finding,不在本 scope 修):`dto::Diagnosis` 没有 core 的
/// `severity`(严重度只能按置信度折算,阈值是债)与 `cause.key`(`title`/`hint` 只能当数据画)。
pub(super) fn logs_model(state: &AppState) -> pages::Logs {
let report = state.diagnosis.as_ref();
let current = state.selected_session.as_deref();
let session = report
.and_then(|report| state.log_sessions.iter().find(|s| s.id == report.session))
.or_else(|| {
state
.log_sessions
.iter()
.find(|s| Some(s.id.as_str()) == current)
});
pages::Logs {
instance: session.map(|s| s.instance.clone()),
when: session.map(|s| s.when.clone()),
outcome: session.map(|s| log_outcome(s.outcome)),
lines: state
.log_lines
.iter()
.map(|line| pages::Line {
text: line.text.clone(),
level: log_level(line.level),
})
.collect(),
findings: report
.map(|report| diagnosis_of(&report.diagnosis))
.into_iter()
.collect(),
..pages::Logs::default()
}
}
/// 日志等级:presenter 枚举 → 页面等级(**结构化出口**,不是认字符串)。
fn log_level(level: LogLevel) -> pages::Level {
match level {
LogLevel::Debug => pages::Level::Debug,
LogLevel::Info => pages::Level::Info,
LogLevel::Warn => pages::Level::Warn,
LogLevel::Error => pages::Level::Error,
}
}
/// 会话结局:presenter 枚举 → 页面结局(文案走 `LaunchOutcome*` key,页面不画 core 的中文串)。
fn log_outcome(outcome: LogOutcome) -> pages::Outcome {
match outcome {
LogOutcome::Ok => pages::Outcome::Ok,
LogOutcome::Crashed => pages::Outcome::Crashed,
LogOutcome::SilentFail => pages::Outcome::SilentFail,
}
}
/// 一条诊断结论 → 页面结论(严重度折算见 [`logs_model`] 的文档)。
fn diagnosis_of(diagnosis: &Diagnosis) -> pages::Finding {
pages::Finding {
rule: diagnosis.rule.clone(),
severity: pages::Severity::of_confidence(diagnosis.confidence),
title: diagnosis.title.clone(),
hint: diagnosis.hint.clone(),
confidence: diagnosis.confidence,
action: diagnosis.fix.clone(),
}
}