nd_300/render/
user_mode.rs1use 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 builder = builder.span_row(" DIAGNOSTIC SUMMARY").divider();
16
17 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 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 let lines: Vec<&str> = result.summary.split('\n').collect();
55 let mut b = builder.row(&label, lines[0]);
56
57 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}