Skip to main content

rigsql_output/
human.rs

1use std::path::Path;
2
3use rigsql_rules::LintViolation;
4
5/// Human-readable terminal output formatter.
6pub struct HumanFormatter {
7    use_color: bool,
8}
9
10impl HumanFormatter {
11    pub fn new(use_color: bool) -> Self {
12        Self { use_color }
13    }
14
15    /// Format violations for a single file.
16    pub fn format_file(&self, path: &Path, source: &str, violations: &[LintViolation]) -> String {
17        if violations.is_empty() {
18            return String::new();
19        }
20
21        let mut out = String::new();
22
23        // File header
24        if self.use_color {
25            out.push_str(&format!("\x1b[1m{}\x1b[0m\n", path.display()));
26        } else {
27            out.push_str(&format!("{}\n", path.display()));
28        }
29
30        for v in violations {
31            let (line, col) = v.line_col(source);
32            let msg = rigsql_i18n::rule_message(&v.message_key, &v.message_params, &v.message);
33
34            if self.use_color {
35                out.push_str(&format!(
36                    "  \x1b[90mL{:>4}:{:<3}\x1b[0m | \x1b[33m{}\x1b[0m | {}\n",
37                    line, col, v.rule_code, msg
38                ));
39            } else {
40                out.push_str(&format!(
41                    "  L{:>4}:{:<3} | {} | {}\n",
42                    line, col, v.rule_code, msg
43                ));
44            }
45        }
46
47        out
48    }
49
50    /// Format a summary line.
51    pub fn format_summary(
52        &self,
53        total_files: usize,
54        files_with_violations: usize,
55        total_violations: usize,
56    ) -> String {
57        let status = if total_violations == 0 {
58            let msg = rigsql_i18n::t("cli.all_checks_passed");
59            if self.use_color {
60                format!("\x1b[32m{msg}\x1b[0m")
61            } else {
62                msg
63            }
64        } else {
65            let template = rigsql_i18n::t("cli.found_violations");
66            let msg = template
67                .replace("%{violations}", &total_violations.to_string())
68                .replace("%{files_with}", &files_with_violations.to_string())
69                .replace("%{total}", &total_files.to_string());
70            if self.use_color {
71                format!("\x1b[31m{msg}\x1b[0m")
72            } else {
73                msg
74            }
75        };
76
77        format!("\n{}\n", status)
78    }
79}