aster/diagnostics/
report.rs1use super::checker::{run_diagnostics, CheckStatus, DiagnosticCheck};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Default)]
10pub struct DiagnosticOptions {
11 pub verbose: bool,
13 pub json: bool,
15 pub fix: bool,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct SystemInfo {
22 pub memory: MemoryInfo,
24 pub cpu: CpuInfo,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct MemoryInfo {
31 pub total: String,
32 pub free: String,
33 pub used: String,
34 pub percent_used: f64,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct CpuInfo {
40 pub model: String,
41 pub cores: usize,
42 pub load_average: Vec<f64>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct DiagnosticReport {
48 pub timestamp: i64,
50 pub version: String,
52 pub platform: String,
54 pub checks: Vec<DiagnosticCheck>,
56 pub summary: ReportSummary,
58 pub system_info: Option<SystemInfo>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct ReportSummary {
65 pub passed: usize,
66 pub warnings: usize,
67 pub failed: usize,
68}
69
70impl DiagnosticReport {
71 pub fn generate(options: &DiagnosticOptions) -> Self {
73 let checks = run_diagnostics();
74
75 let summary = ReportSummary {
76 passed: checks
77 .iter()
78 .filter(|c| c.status == CheckStatus::Pass)
79 .count(),
80 warnings: checks
81 .iter()
82 .filter(|c| c.status == CheckStatus::Warn)
83 .count(),
84 failed: checks
85 .iter()
86 .filter(|c| c.status == CheckStatus::Fail)
87 .count(),
88 };
89
90 let system_info = if options.verbose {
91 Some(Self::collect_system_info())
92 } else {
93 None
94 };
95
96 Self {
97 timestamp: chrono::Utc::now().timestamp(),
98 version: env!("CARGO_PKG_VERSION").to_string(),
99 platform: format!("{} {}", std::env::consts::OS, std::env::consts::ARCH),
100 checks,
101 summary,
102 system_info,
103 }
104 }
105
106 fn collect_system_info() -> SystemInfo {
107 SystemInfo {
108 memory: MemoryInfo {
109 total: "未知".to_string(),
110 free: "未知".to_string(),
111 used: "未知".to_string(),
112 percent_used: 0.0,
113 },
114 cpu: CpuInfo {
115 model: "未知".to_string(),
116 cores: std::thread::available_parallelism()
117 .map(|n| n.get())
118 .unwrap_or(1),
119 load_average: vec![0.0, 0.0, 0.0],
120 },
121 }
122 }
123}
124
125pub fn format_diagnostic_report(report: &DiagnosticReport, options: &DiagnosticOptions) -> String {
127 if options.json {
128 return serde_json::to_string_pretty(report).unwrap_or_default();
129 }
130
131 let mut lines = Vec::new();
132
133 lines.push("╭─────────────────────────────────────────────╮".to_string());
134 lines.push("│ Aster 诊断报告 │".to_string());
135 lines.push("╰─────────────────────────────────────────────╯".to_string());
136 lines.push(String::new());
137 lines.push(format!(" 版本: {}", report.version));
138 lines.push(format!(" 平台: {}", report.platform));
139
140 if let Some(ref sys_info) = report.system_info {
141 lines.push(String::new());
142 lines.push(" 系统信息:".to_string());
143 lines.push(format!(" CPU 核心: {}", sys_info.cpu.cores));
144 }
145
146 lines.push(String::new());
147 lines.push("─────────────────────────────────────────────".to_string());
148 lines.push(String::new());
149
150 for check in &report.checks {
151 let icon = match check.status {
152 CheckStatus::Pass => "✓",
153 CheckStatus::Warn => "⚠",
154 CheckStatus::Fail => "✗",
155 };
156 lines.push(format!(" {} {}: {}", icon, check.name, check.message));
157
158 if options.verbose {
159 if let Some(ref details) = check.details {
160 lines.push(format!(" └─ {}", details));
161 }
162 if let Some(ref fix) = check.fix {
163 lines.push(format!(" 💡 修复: {}", fix));
164 }
165 }
166 }
167
168 lines.push(String::new());
169 lines.push("─────────────────────────────────────────────".to_string());
170 lines.push(String::new());
171 lines.push(format!(
172 " 摘要: {} 通过, {} 警告, {} 失败",
173 report.summary.passed, report.summary.warnings, report.summary.failed
174 ));
175 lines.push(String::new());
176
177 lines.join("\n")
178}