modde_ui/views/
diagnostics.rs1use iced::widget::{button, column, container, row, scrollable, text};
2use iced::{color, Alignment, Element, Length};
3
4use crate::app::Message;
5
6#[derive(Debug, Clone)]
8pub struct DiagnosticEntry {
9 pub severity: DiagnosticSeverity,
10 pub message: String,
11}
12
13#[derive(Debug, Clone)]
15pub enum DiagnosticSeverity {
16 Info,
17 Warning,
18 Error,
19}
20
21#[derive(Debug, Clone, Default)]
23pub enum DiagnosticsState {
24 #[default]
25 Idle,
26 Running,
27 Complete(Vec<DiagnosticEntry>),
28}
29
30pub fn view(state: &DiagnosticsState) -> Element<'_, Message> {
32 let running = matches!(state, DiagnosticsState::Running);
33
34 let title_bar = row![
35 text("Diagnostics").size(20),
36 iced::widget::space::horizontal(),
37 button(
38 text(if running {
39 "Running..."
40 } else {
41 "Run Diagnostics"
42 })
43 .size(14)
44 )
45 .on_press_maybe(if running {
46 None
47 } else {
48 Some(Message::RunDiagnostics)
49 })
50 .style(button::primary)
51 .padding([6, 14]),
52 ]
53 .align_y(Alignment::Center);
54
55 let content: Element<Message> = match state {
56 DiagnosticsState::Idle | DiagnosticsState::Running => container(
57 text("Click 'Run Diagnostics' to scan for common modding issues.").size(14),
58 )
59 .padding(20)
60 .width(Length::Fill)
61 .center_x(Length::Fill)
62 .into(),
63
64 DiagnosticsState::Complete(entries) => {
65 if entries.is_empty() {
66 container(
67 text("No issues found!")
68 .size(14)
69 .color(color!(0x88CC88)),
70 )
71 .padding(20)
72 .width(Length::Fill)
73 .center_x(Length::Fill)
74 .into()
75 } else {
76 let rows =
77 entries
78 .iter()
79 .fold(column![].spacing(4), |col, entry| {
80 let (icon, icon_color) = match entry.severity {
81 DiagnosticSeverity::Info => ("INFO", color!(0x88AACC)),
82 DiagnosticSeverity::Warning => ("WARN", color!(0xFFAA44)),
83 DiagnosticSeverity::Error => ("ERR ", color!(0xFF4444)),
84 };
85
86 let entry_row = row![
87 text(icon).size(12).color(icon_color).width(Length::Fixed(40.0)),
88 text(&entry.message).size(13).width(Length::Fill),
89 ]
90 .spacing(8)
91 .padding([4, 8]);
92
93 col.push(entry_row)
94 });
95
96 let summary = {
97 let errors = entries
98 .iter()
99 .filter(|e| matches!(e.severity, DiagnosticSeverity::Error))
100 .count();
101 let warnings = entries
102 .iter()
103 .filter(|e| matches!(e.severity, DiagnosticSeverity::Warning))
104 .count();
105 let infos = entries
106 .iter()
107 .filter(|e| matches!(e.severity, DiagnosticSeverity::Info))
108 .count();
109 text(format!(
110 "{} error(s), {} warning(s), {} info(s)",
111 errors, warnings, infos
112 ))
113 .size(12)
114 };
115
116 column![
117 summary,
118 scrollable(rows.padding(8)).height(Length::Fill),
119 ]
120 .spacing(8)
121 .into()
122 }
123 }
124 };
125
126 column![title_bar, iced::widget::rule::horizontal(1), content]
127 .spacing(8)
128 .padding(16)
129 .width(Length::Fill)
130 .height(Length::Fill)
131 .into()
132}