supa_mdx_lint/
output.rs

1use std::{collections::HashSet, str::FromStr};
2
3use anyhow::Result;
4
5use crate::{app_error::PublicError, errors::LintError, ConfigMetadata, LintLevel};
6
7pub mod markdown;
8#[cfg(feature = "pretty")]
9pub mod pretty;
10pub mod rdf;
11pub mod simple;
12
13#[derive(Debug)]
14pub struct LintOutput {
15    file_path: String,
16    errors: Vec<LintError>,
17}
18
19impl LintOutput {
20    pub(crate) fn new(file_path: impl AsRef<str>, errors: Vec<LintError>) -> Self {
21        Self {
22            file_path: file_path.as_ref().to_string(),
23            errors,
24        }
25    }
26
27    pub fn file_path(&self) -> &str {
28        &self.file_path
29    }
30
31    pub fn errors(&self) -> &[LintError] {
32        &self.errors
33    }
34}
35
36pub struct OutputSummary {
37    pub num_files: usize,
38    pub num_warnings: usize,
39    pub num_errors: usize,
40}
41
42pub trait OutputFormatter: Send + Sync + std::fmt::Debug {
43    fn id(&self) -> &'static str;
44    fn format(&self, output: &[LintOutput], metadata: &ConfigMetadata) -> Result<String>;
45    fn should_log_metadata(&self) -> bool;
46
47    fn get_summary(&self, output: &[LintOutput]) -> OutputSummary {
48        let mut seen_files = HashSet::<&str>::new();
49        let mut num_errors = 0;
50        let mut num_warnings = 0;
51
52        for o in output {
53            seen_files.insert(&o.file_path);
54            for error in &o.errors {
55                match error.level {
56                    LintLevel::Error => num_errors += 1,
57                    LintLevel::Warning => num_warnings += 1,
58                }
59            }
60        }
61
62        OutputSummary {
63            num_files: seen_files.len(),
64            num_warnings,
65            num_errors,
66        }
67    }
68}
69
70#[doc(hidden)]
71pub mod internal {
72    //! Contains internal implementations that are needed for the supa-mdx-lint
73    //! binary. Should **not** be used by library users as API stability is
74    //! not guaranteed.
75
76    use super::*;
77
78    #[derive(Debug)]
79    pub struct NativeOutputFormatter(Box<dyn OutputFormatter>);
80
81    impl Clone for NativeOutputFormatter {
82        fn clone(&self) -> Self {
83            // Clone is required for clap parsing.
84            //
85            // These are zero-sized types with no state information, so
86            // cloning by recreating (a) is efficient and (b) will not cause
87            // any unexpected logic errors.
88            match self.0.id() {
89                "markdown" => Self(Box::new(markdown::MarkdownFormatter)),
90                #[cfg(feature = "pretty")]
91                "pretty" => Self(Box::new(pretty::PrettyFormatter)),
92                "rdf" => Self(Box::new(rdf::RdfFormatter)),
93                "simple" => Self(Box::new(simple::SimpleFormatter)),
94                _ => panic!("NativeOutputFormatter should only be used to wrap the native output formats, not a user-provided custom format"),
95            }
96        }
97    }
98
99    impl std::ops::Deref for NativeOutputFormatter {
100        type Target = Box<dyn OutputFormatter>;
101
102        fn deref(&self) -> &Self::Target {
103            &self.0
104        }
105    }
106
107    impl std::ops::DerefMut for NativeOutputFormatter {
108        fn deref_mut(&mut self) -> &mut Self::Target {
109            &mut self.0
110        }
111    }
112
113    impl FromStr for NativeOutputFormatter {
114        type Err = PublicError;
115
116        fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
117            match s {
118                "markdown" => Ok(NativeOutputFormatter(Box::new(markdown::MarkdownFormatter))),
119                #[cfg(feature = "pretty")]
120                "pretty" => Ok(NativeOutputFormatter(Box::new(pretty::PrettyFormatter))),
121                "rdf" => Ok(NativeOutputFormatter(Box::new(rdf::RdfFormatter))),
122                "simple" => Ok(NativeOutputFormatter(Box::new(simple::SimpleFormatter))),
123                s => Err(PublicError::VariantNotFound(s.to_string())),
124            }
125        }
126    }
127}