Skip to main content

core_invoice/
report.rs

1use crate::bt::Path;
2use std::fmt;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
5pub enum Severity {
6    Fatal,
7    Warning,
8    Info,
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Source {
13    Both,
14    StandardOnly,
15    ArtefactOnly,
16    Crate,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct Finding {
21    pub id: &'static str,
22    pub severity: Severity,
23    pub path: Path,
24    pub message: String,
25    pub detail: Option<String>,
26    pub hint: Option<String>,
27}
28
29impl Finding {
30    pub fn fatal(id: &'static str, path: Path, message: impl Into<String>) -> Self {
31        Self {
32            id,
33            severity: Severity::Fatal,
34            path,
35            message: message.into(),
36            detail: None,
37            hint: None,
38        }
39    }
40
41    pub fn warning(id: &'static str, path: Path, message: impl Into<String>) -> Self {
42        Self {
43            id,
44            severity: Severity::Warning,
45            path,
46            message: message.into(),
47            detail: None,
48            hint: None,
49        }
50    }
51}
52
53#[derive(Debug, Clone, Default, PartialEq, Eq)]
54pub struct Report {
55    pub findings: Vec<Finding>,
56    pub profile_slug: &'static str,
57    pub rules_checked: usize,
58}
59
60impl Report {
61    pub fn ok(&self) -> bool {
62        !self.findings.iter().any(|f| f.severity == Severity::Fatal)
63    }
64
65    pub fn push(&mut self, finding: Finding) {
66        self.findings.push(finding);
67    }
68
69    pub fn sort_stable(&mut self) {
70        self.findings.sort_by(|a, b| {
71            a.severity
72                .cmp(&b.severity)
73                .then_with(|| a.path.to_string().cmp(&b.path.to_string()))
74                .then_with(|| a.id.cmp(b.id))
75        });
76    }
77}
78
79impl fmt::Display for Finding {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        write!(f, "[{}] {} — {}", self.id, self.path, self.message)?;
82        if let Some(d) = &self.detail {
83            write!(f, " ({d})")?;
84        }
85        Ok(())
86    }
87}
88
89impl fmt::Display for Report {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        if self.ok() {
92            return write!(f, "valid");
93        }
94        for finding in &self.findings {
95            writeln!(f, "{finding}")?;
96        }
97        Ok(())
98    }
99}