Skip to main content

nd_300/render/
user_mode.rs

1use crate::config::Config;
2use crate::diagnostics::{DiagnosticResults, DiagnosticStatus};
3use crate::render::color::{colorize_status, dim};
4use crate::render::table::ReportBuilder;
5
6pub fn render(results: &DiagnosticResults, config: &Config) -> String {
7    let label_width = 14;
8    let data_width = 40;
9    let chars = config.box_chars();
10
11    let mut builder = ReportBuilder::new(label_width, data_width, chars)
12        .header(config.title(), config.subtitle());
13
14    // Diagnostic summary header
15    builder = builder.span_row("  DIAGNOSTIC SUMMARY").divider();
16
17    // Render each diagnostic category
18    builder = render_diagnostic_row(builder, &results.adapters, config);
19    builder = render_diagnostic_row(builder, &results.interfaces, config);
20    builder = render_diagnostic_row(builder, &results.gateway, config);
21    builder = render_diagnostic_row(builder, &results.dns, config);
22    builder = render_diagnostic_row(builder, &results.public_ip, config);
23    builder = render_diagnostic_row(builder, &results.latency, config);
24    builder = render_diagnostic_row(builder, &results.speed, config);
25    builder = render_diagnostic_row(builder, &results.ports, config);
26
27    // Overall status
28    let (fail_count, warn_count) = count_issues(results);
29    let overall = format_overall(fail_count, warn_count, config);
30
31    builder = builder.divider();
32    builder = builder.span_row(&format!("  OVERALL: {}", overall));
33
34    if fail_count > 0 {
35        builder = builder.span_row(&dim("  Run 'nd300 -f' to attempt automatic fixes", config));
36    }
37
38    let mut output = builder.finish();
39    output.push('\n');
40    output
41}
42
43fn render_diagnostic_row(
44    builder: ReportBuilder,
45    result: &crate::diagnostics::DiagnosticResult,
46    config: &Config,
47) -> ReportBuilder {
48    let icon = config.status_chars(&result.status);
49    let colored_icon = colorize_status(icon, &result.status, config);
50
51    let label = format!("{} {}", colored_icon, result.category);
52
53    // Handle multi-line summaries (speed test can have a second line)
54    let lines: Vec<&str> = result.summary.split('\n').collect();
55    let mut b = builder.row(&label, lines[0]);
56
57    // Render additional lines with empty label
58    for line in lines.iter().skip(1) {
59        b = b.row("", line);
60    }
61
62    b
63}
64
65fn count_issues(results: &DiagnosticResults) -> (usize, usize) {
66    let statuses = [
67        &results.adapters.status,
68        &results.interfaces.status,
69        &results.gateway.status,
70        &results.dns.status,
71        &results.public_ip.status,
72        &results.latency.status,
73        &results.speed.status,
74        &results.ports.status,
75    ];
76
77    let fails = statuses
78        .iter()
79        .filter(|s| ***s == DiagnosticStatus::Fail)
80        .count();
81    let warns = statuses
82        .iter()
83        .filter(|s| ***s == DiagnosticStatus::Warn)
84        .count();
85
86    (fails, warns)
87}
88
89fn format_overall(fails: usize, warns: usize, config: &Config) -> String {
90    if fails > 0 && warns > 0 {
91        let text = format!(
92            "{} failure{}, {} warning{}",
93            fails,
94            if fails > 1 { "s" } else { "" },
95            warns,
96            if warns > 1 { "s" } else { "" }
97        );
98        colorize_status(&text, &DiagnosticStatus::Fail, config)
99    } else if fails > 0 {
100        let text = format!(
101            "{} failure{} detected",
102            fails,
103            if fails > 1 { "s" } else { "" }
104        );
105        colorize_status(&text, &DiagnosticStatus::Fail, config)
106    } else if warns > 0 {
107        let text = format!(
108            "{} warning{} detected",
109            warns,
110            if warns > 1 { "s" } else { "" }
111        );
112        colorize_status(&text, &DiagnosticStatus::Warn, config)
113    } else {
114        colorize_status("All diagnostics passed", &DiagnosticStatus::Ok, config)
115    }
116}