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 mut output_stream = self.output_stream.lock();
24        output_stream
25            .write_all(s.as_bytes())
26            .and_then(|_| output_stream.flush())
27            .unwrap_or_else(|e| panic!("failed to emit error: {e}"));
28    }
29}
30
31impl Formatter for GithubAnnotationNativeFormatter {
32    fn dispatch_template_header(
33        &self,
34        _f_name: String,
35        _linter_config: FluffConfig,
36        _file_config: FluffConfig,
37    ) {
38        // No-op
39    }
40
41    fn dispatch_parse_header(&self, _f_name: String) {
42        // No-op
43    }
44
45    fn dispatch_file_violations(&self, linted_file: &LintedFile, _only_fixable: bool) {
46        let mut violations = linted_file.get_violations(None);
47
48        violations.sort_by(|a, b| {
49            a.line_no
50                .cmp(&b.line_no)
51                .then_with(|| a.line_pos.cmp(&b.line_pos))
52                .then_with(|| {
53                    let b = b.rule.as_ref().unwrap().code;
54                    a.rule.as_ref().unwrap().code.cmp(b)
55                })
56        });
57
58        for violation in violations {
59            let message = format!(
60                "::error title=sqruff,file={},line={},col={}::{}: {}\n",
61                linted_file.path,
62                violation.line_no,
63                violation.line_pos,
64                violation.rule.as_ref().unwrap().code,
65                violation.description
66            );
67
68            self.dispatch(&message);
69            self.has_fail.store(true, Ordering::SeqCst);
70        }
71    }
72
73    fn completion_message(&self) {
74        // No-op
75    }
76}