Skip to main content

weggli_ruleset/
reporting.rs

1use std::borrow::Cow;
2use std::fmt::Debug;
3use std::sync::Arc;
4
5use rustc_hash::FxHashSet;
6use serde::{Deserialize, Serialize};
7use weggli::result::QueryResult;
8
9use crate::matcher::RuleMatch;
10use crate::rule::Severity;
11
12#[derive(Deserialize, Serialize)]
13pub struct RuleMatchReport<'a> {
14    rule: Cow<'a, str>,
15    checker: Cow<'a, str>,
16    #[serde(skip_serializing_if = "str::is_empty")]
17    description: Cow<'a, str>,
18    #[serde(skip_serializing_if = "FxHashSet::<String>::is_empty")]
19    tags: Cow<'a, FxHashSet<String>>,
20    severity: Severity,
21    source: Arc<str>,
22    #[serde(rename = "match")]
23    match_result: Cow<'a, QueryResult>,
24}
25
26impl<'a> Debug for RuleMatchReport<'a> {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        let mut m = f.debug_struct("RuleMatchReport");
29
30        m.field("rule", &self.rule as _);
31        m.field("checker", &self.checker as _);
32
33        if let Some(ref description) = self.description() {
34            m.field("description", description as _);
35        }
36
37        m.field("tags", self.tags());
38        m.field("severity", &self.severity as _);
39        m.field("matches", &self.match_result as _);
40
41        m.finish_non_exhaustive()
42    }
43}
44
45impl<'a> RuleMatchReport<'a> {
46    pub fn new(m: &'a RuleMatch) -> Self {
47        Self {
48            rule: Cow::Borrowed(m.rule().id()),
49            description: Cow::Borrowed(m.rule().description().unwrap_or_default()),
50            checker: Cow::Borrowed(m.checker().name()),
51            tags: Cow::Borrowed(m.rule().tags()),
52            severity: m.rule().severity(),
53            source: m.source(),
54            match_result: Cow::Borrowed(m.result()),
55        }
56    }
57
58    pub fn rule(&self) -> &str {
59        &self.rule
60    }
61
62    pub fn checker(&self) -> &str {
63        &self.checker
64    }
65
66    pub fn description(&self) -> Option<&str> {
67        if self.description.is_empty() {
68            None
69        } else {
70            Some(&self.description)
71        }
72    }
73
74    pub fn severity(&self) -> Severity {
75        self.severity
76    }
77
78    pub fn tags(&self) -> &FxHashSet<String> {
79        &self.tags
80    }
81
82    pub fn source(&self) -> &str {
83        &self.source
84    }
85
86    pub fn result(&self) -> &QueryResult {
87        &self.match_result
88    }
89
90    pub fn display(&self, before: usize, after: usize, line_numbers: bool) -> String {
91        self.match_result
92            .display(&self.source, before, after, line_numbers)
93    }
94
95    pub fn into_owned(self) -> RuleMatchReport<'static> {
96        RuleMatchReport {
97            rule: self.rule.into_owned().into(),
98            description: self.description.into_owned().into(),
99            checker: self.checker.into_owned().into(),
100            tags: Cow::Owned(self.tags.into_owned()),
101            severity: self.severity,
102            source: self.source,
103            match_result: Cow::Owned(self.match_result.into_owned()),
104        }
105    }
106}