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 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 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}