1use std::path::Path;
2
3use rigsql_rules::LintViolation;
4
5pub 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 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 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 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}