use std::fmt;
use super::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum Severity {
Error,
EvaluationError,
Warning,
Unsupported,
}
impl fmt::Display for Severity {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let text = match self {
Self::Error => "error",
Self::EvaluationError => "evaluation-error",
Self::Warning => "warning",
Self::Unsupported => "unsupported",
};
formatter.write_str(text)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Finding {
pub severity: Severity,
pub rule: String,
pub path: Path,
pub message: String,
}
impl Finding {
#[must_use]
pub fn error(rule: impl Into<String>, path: Path, message: impl Into<String>) -> Self {
Self {
severity: Severity::Error,
rule: rule.into(),
path,
message: message.into(),
}
}
#[must_use]
pub fn warning(rule: impl Into<String>, path: Path, message: impl Into<String>) -> Self {
Self {
severity: Severity::Warning,
rule: rule.into(),
path,
message: message.into(),
}
}
#[must_use]
pub fn evaluation_error(
rule: impl Into<String>,
path: Path,
message: impl Into<String>,
) -> Self {
Self {
severity: Severity::EvaluationError,
rule: rule.into(),
path,
message: message.into(),
}
}
#[must_use]
pub fn unsupported(rule: impl Into<String>, path: Path, message: impl Into<String>) -> Self {
Self {
severity: Severity::Unsupported,
rule: rule.into(),
path,
message: message.into(),
}
}
}
impl fmt::Display for Finding {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"{}: {} at {}: {}",
self.severity, self.rule, self.path, self.message
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use ifc_model::EntityId;
#[test]
fn errors_sort_before_warnings_before_unsupported() {
let mut severities = [
Severity::Unsupported,
Severity::Warning,
Severity::EvaluationError,
Severity::Error,
];
severities.sort();
assert_eq!(
severities,
[
Severity::Error,
Severity::EvaluationError,
Severity::Warning,
Severity::Unsupported
]
);
}
#[test]
fn every_severity_renders_distinctly() {
let rendered: Vec<String> = [
Severity::Error,
Severity::EvaluationError,
Severity::Warning,
Severity::Unsupported,
]
.iter()
.map(ToString::to_string)
.collect();
let mut unique = rendered.clone();
unique.sort();
unique.dedup();
assert_eq!(unique.len(), rendered.len(), "{rendered:?}");
}
#[test]
fn a_finding_names_where_it_applies() {
let finding = Finding::error(
"structure.dangling",
Path::Attribute {
entity: EntityId(12),
index: 3,
name: Some("Representation".into()),
},
"points at #99, which does not exist",
);
assert_eq!(finding.path.to_string(), "#12.Representation");
assert_eq!(finding.severity, Severity::Error);
}
}