Skip to main content

aster/diagnostics/
report.rs

1//! 诊断报告
2//!
3//! 生成和格式化诊断报告
4
5use super::checker::{run_diagnostics, CheckStatus, DiagnosticCheck};
6use serde::{Deserialize, Serialize};
7
8/// 诊断选项
9#[derive(Debug, Clone, Default)]
10pub struct DiagnosticOptions {
11    /// 详细模式
12    pub verbose: bool,
13    /// JSON 输出
14    pub json: bool,
15    /// 自动修复
16    pub fix: bool,
17}
18
19/// 系统信息
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct SystemInfo {
22    /// 内存信息
23    pub memory: MemoryInfo,
24    /// CPU 信息
25    pub cpu: CpuInfo,
26}
27
28/// 内存信息
29#[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/// CPU 信息
38#[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/// 诊断报告
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct DiagnosticReport {
48    /// 时间戳
49    pub timestamp: i64,
50    /// 版本
51    pub version: String,
52    /// 平台
53    pub platform: String,
54    /// 检查结果
55    pub checks: Vec<DiagnosticCheck>,
56    /// 摘要
57    pub summary: ReportSummary,
58    /// 系统信息(详细模式)
59    pub system_info: Option<SystemInfo>,
60}
61
62/// 报告摘要
63#[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    /// 生成诊断报告
72    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
125/// 格式化诊断报告
126pub 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}