use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Severity {
Info,
Warning,
Error,
}
impl Severity {
pub fn name(&self) -> &'static str {
match self {
Self::Info => "info",
Self::Warning => "warning",
Self::Error => "error",
}
}
}
broadcast_common::impl_spec_display!(Severity);
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Location {
pub packet: usize,
pub pid: u16,
}
impl Location {
pub fn new(packet: usize, pid: u16) -> Self {
Self { packet, pid }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Finding {
pub severity: Severity,
pub location: Location,
pub rule_id: String,
pub message: String,
}
impl Finding {
pub fn new(
severity: Severity,
location: Location,
rule_id: impl Into<String>,
message: impl Into<String>,
) -> Self {
Self {
severity,
location,
rule_id: rule_id.into(),
message: message.into(),
}
}
}
impl fmt::Display for Finding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[{}] [{}] packet={} pid=0x{:04X} {}",
self.severity, self.rule_id, self.location.packet, self.location.pid, self.message
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Report {
findings: Vec<Finding>,
}
impl Report {
pub const fn new() -> Self {
Self {
findings: Vec::new(),
}
}
pub fn push(&mut self, finding: Finding) {
self.findings.push(finding);
}
pub fn findings(&self) -> &[Finding] {
&self.findings
}
pub fn len(&self) -> usize {
self.findings.len()
}
pub fn is_empty(&self) -> bool {
self.findings.is_empty()
}
}
impl Default for Report {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for Report {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.findings.is_empty() {
writeln!(f, "No issues found.")?;
return Ok(());
}
let errs = self
.findings
.iter()
.filter(|x| x.severity == Severity::Error)
.count();
let warns = self
.findings
.iter()
.filter(|x| x.severity == Severity::Warning)
.count();
let infos = self
.findings
.iter()
.filter(|x| x.severity == Severity::Info)
.count();
writeln!(
f,
"Findings: {errs} error(s), {warns} warning(s), {infos} info(s)"
)?;
for (i, finding) in self.findings.iter().enumerate() {
writeln!(f, "{:>4}. {}", i + 1, finding)?;
}
Ok(())
}
}