Skip to main content

xtask/patterns/
reporter.rs

1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2enum FindingLevel {
3    Ok,
4    Warn,
5    Fail,
6}
7
8struct PatternFinding {
9    level: FindingLevel,
10    check: &'static str,
11    message: String,
12}
13
14#[derive(Default)]
15pub(super) struct PatternReporter {
16    findings: Vec<PatternFinding>,
17}
18
19impl PatternReporter {
20    pub(super) fn ok(&mut self, check: &'static str, message: impl Into<String>) {
21        self.findings.push(PatternFinding {
22            level: FindingLevel::Ok,
23            check,
24            message: message.into(),
25        });
26    }
27
28    pub(super) fn warn(&mut self, check: &'static str, message: impl Into<String>) {
29        self.findings.push(PatternFinding {
30            level: FindingLevel::Warn,
31            check,
32            message: message.into(),
33        });
34    }
35
36    pub(super) fn fail(&mut self, check: &'static str, message: impl Into<String>) {
37        self.findings.push(PatternFinding {
38            level: FindingLevel::Fail,
39            check,
40            message: message.into(),
41        });
42    }
43
44    pub(super) fn print(&self) {
45        for finding in &self.findings {
46            println!(
47                "{}: {}: {}",
48                finding.level.as_str(),
49                finding.check,
50                finding.message
51            );
52        }
53    }
54
55    pub(super) fn print_json(&self) {
56        println!("{{");
57        println!("  \"findings\": [");
58        for (index, finding) in self.findings.iter().enumerate() {
59            let comma = if index + 1 == self.findings.len() {
60                ""
61            } else {
62                ","
63            };
64            println!(
65                "    {{\"level\":\"{}\",\"check\":\"{}\",\"message\":\"{}\"}}{}",
66                finding.level.as_str(),
67                json_escape(finding.check),
68                json_escape(&finding.message),
69                comma
70            );
71        }
72        println!("  ],");
73        println!("  \"has_failures\": {},", self.has_failures());
74        println!("  \"has_warnings\": {}", self.has_warnings());
75        println!("}}");
76    }
77
78    pub(super) fn has_failures(&self) -> bool {
79        self.findings
80            .iter()
81            .any(|finding| finding.level == FindingLevel::Fail)
82    }
83
84    pub(super) fn has_warnings(&self) -> bool {
85        self.findings
86            .iter()
87            .any(|finding| finding.level == FindingLevel::Warn)
88    }
89}
90
91impl FindingLevel {
92    fn as_str(self) -> &'static str {
93        match self {
94            FindingLevel::Ok => "OK",
95            FindingLevel::Warn => "WARN",
96            FindingLevel::Fail => "FAIL",
97        }
98    }
99}
100
101fn json_escape(value: &str) -> String {
102    value
103        .chars()
104        .flat_map(|ch| match ch {
105            '\\' => "\\\\".chars().collect::<Vec<_>>(),
106            '"' => "\\\"".chars().collect(),
107            '\n' => "\\n".chars().collect(),
108            '\r' => "\\r".chars().collect(),
109            '\t' => "\\t".chars().collect(),
110            other => vec![other],
111        })
112        .collect()
113}