sqruff_lib/cli/
github_annotation_native_formatter.rs

1use crate::core::config::FluffConfig;
2use crate::core::linter::linted_file::LintedFile;
3use std::io::{Stderr, Write};
4use std::sync::atomic::{AtomicBool, Ordering};
5
6use super::formatters::Formatter;
7
8#[derive(Debug)]
9pub struct GithubAnnotationNativeFormatter {
10    output_stream: Stderr,
11    pub has_fail: AtomicBool,
12}
13
14impl GithubAnnotationNativeFormatter {
15    pub fn new(stderr: Stderr) -> Self {
16        Self {
17            output_stream: stderr,
18            has_fail: AtomicBool::new(false),
19        }
20    }
21
22    fn dispatch(&self, s: &str) {
23        let _ignored = self.output_stream.lock().write(s.as_bytes()).unwrap();
24    }
25}
26
27impl Formatter for GithubAnnotationNativeFormatter {
28    fn dispatch_template_header(
29        &self,
30        _f_name: String,
31        _linter_config: FluffConfig,
32        _file_config: FluffConfig,
33    ) {
34        // No-op
35    }
36
37    fn dispatch_parse_header(&self, _f_name: String) {
38        // No-op
39    }
40
41    fn dispatch_file_violations(&self, linted_file: &LintedFile, _only_fixable: bool) {
42        let mut violations = linted_file.get_violations(None);
43
44        violations.sort_by(|a, b| {
45            a.line_no
46                .cmp(&b.line_no)
47                .then_with(|| a.line_pos.cmp(&b.line_pos))
48                .then_with(|| {
49                    let b = b.rule.as_ref().unwrap().code;
50                    a.rule.as_ref().unwrap().code.cmp(b)
51                })
52        });
53
54        for violation in violations {
55            let message = format!(
56                "::error title=sqruff,file={},line={},col={}::{}: {}\n",
57                linted_file.path,
58                violation.line_no,
59                violation.line_pos,
60                violation.rule.as_ref().unwrap().code,
61                violation.description
62            );
63            self.dispatch(&message);
64            if !violation.ignore && !violation.warning {
65                self.has_fail.store(true, Ordering::SeqCst);
66            }
67        }
68    }
69
70    fn has_fail(&self) -> bool {
71        self.has_fail.load(Ordering::SeqCst)
72    }
73
74    fn completion_message(&self) {
75        // No-op
76    }
77}