Skip to main content

ktrs_cli/ktlint/reporter/
format.rs

1//! Port of `ktlint-cli-reporter-format` (`FormatReporter`, `FormatReporterProvider`).
2
3use std::collections::HashMap;
4
5use crate::ktlint::console::Printer;
6use crate::ktlint::reporter::{Color, KtlintCliError, ReporterOptions, ReporterV2, Status, color_file_name, empty_or_true};
7
8/// Prints one line per file: whether formatting is (or was) needed and whether it is complete.
9pub struct FormatReporter {
10    out: Printer,
11    format: bool,
12    should_color_output: bool,
13    output_color: Color,
14    count_auto_correct_possible_or_done: HashMap<String, usize>,
15    count_can_not_be_auto_corrected: HashMap<String, usize>,
16}
17
18impl FormatReporter {
19    pub fn new(out: Printer, format: bool, should_color_output: bool, output_color: Color) -> FormatReporter {
20        FormatReporter {
21            out,
22            format,
23            should_color_output,
24            output_color,
25            count_auto_correct_possible_or_done: HashMap::new(),
26            count_can_not_be_auto_corrected: HashMap::new(),
27        }
28    }
29
30    fn colored(&self, text: &str) -> String {
31        if self.should_color_output { self.output_color.paint(text) } else { text.to_owned() }
32    }
33}
34
35impl ReporterV2 for FormatReporter {
36    fn on_lint_error(&mut self, file: &str, ktlint_cli_error: &KtlintCliError) {
37        let counts = match ktlint_cli_error.status {
38            Status::LintCanBeAutocorrected | Status::FormatIsAutocorrected => &mut self.count_auto_correct_possible_or_done,
39            _ => &mut self.count_can_not_be_auto_corrected,
40        };
41        *counts.entry(file.to_owned()).or_insert(0) += 1;
42    }
43
44    fn after(&mut self, file: &str) {
45        let can_not_be_autocorrected = self.count_can_not_be_auto_corrected.get(file).copied().unwrap_or(0);
46        let result = match can_not_be_autocorrected {
47            1 if self.format => "Format not completed (1 violation needs manual fixing)".to_owned(),
48            1 => "Format required (1 violation needs manual fixing)".to_owned(),
49            n if n > 1 && self.format => format!("Format not completed ({n} violations need manual fixing)"),
50            n if n > 1 => format!("Format required ({n} violations need manual fixing)"),
51            _ if self.count_auto_correct_possible_or_done.get(file).copied().unwrap_or(0) > 0 => {
52                if self.format {
53                    "Format completed (all violations have been fixed)".to_owned()
54                } else {
55                    "Format required (all violations can be autocorrected)".to_owned()
56                }
57            }
58            _ => "Format not needed (no violations found)".to_owned(),
59        };
60        let line = format!("{}{} {result}", color_file_name(file, |t| self.colored(t)), self.colored(":"));
61        self.out.println(&line);
62    }
63}
64
65pub struct FormatReporterProvider;
66
67impl FormatReporterProvider {
68    pub const ID: &'static str = "format";
69
70    pub fn get(out: Printer, opt: &ReporterOptions) -> Result<FormatReporter, String> {
71        let format = match opt.get("format") {
72            None => {
73                return Err("java.lang.IllegalArgumentException: Format is not specified in config options".to_owned());
74            }
75            Some("true") => true,
76            Some("false") => false,
77            Some(other) => {
78                return Err(format!(
79                    "java.lang.IllegalArgumentException: The string doesn't represent a boolean value: {other}"
80                ));
81            }
82        };
83        let should_color_output = empty_or_true(opt.get("color"));
84        let output_color = Color::from_option(opt.get("color_name"))?;
85        Ok(FormatReporter::new(out, format, should_color_output, output_color))
86    }
87}