1use crate::bt::Path;
4use std::fmt;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
8pub enum Severity {
9 Fatal,
11 Warning,
13 Info,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum Source {
20 Both,
22 StandardOnly,
24 ArtefactOnly,
26 Crate,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct Finding {
33 pub id: &'static str,
35 pub severity: Severity,
37 pub path: Path,
39 pub message: String,
41 pub detail: Option<String>,
43 pub hint: Option<String>,
45}
46
47impl Finding {
48 pub fn fatal(id: &'static str, path: Path, message: impl Into<String>) -> Self {
50 Self {
51 id,
52 severity: Severity::Fatal,
53 path,
54 message: message.into(),
55 detail: None,
56 hint: None,
57 }
58 }
59
60 pub fn warning(id: &'static str, path: Path, message: impl Into<String>) -> Self {
62 Self {
63 id,
64 severity: Severity::Warning,
65 path,
66 message: message.into(),
67 detail: None,
68 hint: None,
69 }
70 }
71
72 pub fn info(id: &'static str, path: Path, message: impl Into<String>) -> Self {
74 Self {
75 id,
76 severity: Severity::Info,
77 path,
78 message: message.into(),
79 detail: None,
80 hint: None,
81 }
82 }
83}
84
85#[derive(Debug, Clone, Default, PartialEq, Eq)]
87pub struct Report {
88 pub findings: Vec<Finding>,
90 pub profile_slug: &'static str,
92 pub rules_checked: usize,
94}
95
96impl Report {
97 pub fn ok(&self) -> bool {
99 !self.findings.iter().any(|f| f.severity == Severity::Fatal)
100 }
101
102 pub fn push(&mut self, finding: Finding) {
104 self.findings.push(finding);
105 }
106
107 pub fn sort_stable(&mut self) {
109 self.findings.sort_by(|a, b| {
110 a.severity
111 .cmp(&b.severity)
112 .then_with(|| a.path.to_string().cmp(&b.path.to_string()))
113 .then_with(|| a.id.cmp(b.id))
114 });
115 }
116}
117
118impl fmt::Display for Finding {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 write!(f, "[{}] {} — {}", self.id, self.path, self.message)?;
121 if let Some(d) = &self.detail {
122 write!(f, " ({d})")?;
123 }
124 Ok(())
125 }
126}
127
128impl fmt::Display for Report {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 if self.ok() {
131 return write!(f, "valid");
132 }
133 for finding in &self.findings {
134 writeln!(f, "{finding}")?;
135 }
136 Ok(())
137 }
138}