xtask/patterns/
reporter.rs1#[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}