Skip to main content

agx_cli/output/
human.rs

1//! Human-readable formatter for ValidationReport.
2
3use agx::preset::validate::{FileReport, FileStatus, Severity, ValidationReport};
4
5/// Format a [`ValidationReport`] for display in a terminal.
6///
7/// Pass `quiet = true` to omit "ok" lines for clean files.
8pub fn format_human(report: &ValidationReport, quiet: bool) -> String {
9    let mut out = String::new();
10
11    for file in &report.files {
12        if file.status == FileStatus::Ok && quiet {
13            continue;
14        }
15        out.push_str(&format_file(file));
16        out.push('\n');
17    }
18
19    // Summary line
20    let summary = &report.summary;
21    if summary.errors == 0 {
22        out.push_str(&format!(
23            "{} file{} checked, all ok\n",
24            summary.total,
25            if summary.total == 1 { "" } else { "s" }
26        ));
27    } else {
28        out.push_str(&format!(
29            "{} file{} checked, {} with error{}\n",
30            summary.total,
31            if summary.total == 1 { "" } else { "s" },
32            summary.errors,
33            if summary.errors == 1 { "" } else { "s" }
34        ));
35    }
36
37    out
38}
39
40fn format_file(file: &FileReport) -> String {
41    let mut out = String::new();
42    if file.status == FileStatus::Ok {
43        out.push_str(&format!("{}: ok\n", file.path));
44        return out;
45    }
46
47    out.push_str(&format!(
48        "{}: {} problem{}\n",
49        file.path,
50        file.diagnostics.len(),
51        if file.diagnostics.len() == 1 { "" } else { "s" }
52    ));
53
54    for diag in &file.diagnostics {
55        let severity_label = match diag.severity {
56            Severity::Error => "error",
57            Severity::Warning => "warning",
58        };
59        out.push_str(&format!(
60            "  {}: {} (line {})\n",
61            severity_label, diag.message, diag.location.line
62        ));
63    }
64
65    out
66}