use std::fmt;
use camino::Utf8PathBuf;
use crate::domain::rule_id::RuleId;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Finding {
pub rule: RuleId,
pub path: Option<Utf8PathBuf>,
pub line: Option<usize>,
pub detail: String,
}
impl Finding {
#[must_use]
pub fn on_file(rule: RuleId, path: impl Into<Utf8PathBuf>, detail: impl Into<String>) -> Self {
Self {
rule,
path: Some(path.into()),
line: None,
detail: detail.into(),
}
}
#[must_use]
pub fn on_line(
rule: RuleId,
path: impl Into<Utf8PathBuf>,
line: usize,
detail: impl Into<String>,
) -> Self {
Self {
rule,
path: Some(path.into()),
line: Some(line),
detail: detail.into(),
}
}
#[must_use]
pub fn global(rule: RuleId, detail: impl Into<String>) -> Self {
Self {
rule,
path: None,
line: None,
detail: detail.into(),
}
}
}
impl fmt::Display for Finding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FAIL {}", self.rule)?;
if let Some(path) = &self.path {
write!(f, " {path}")?;
if let Some(line) = self.line {
write!(f, ":{line}")?;
}
}
if self.detail.is_empty() {
return Ok(());
}
write!(f, ": {}", self.detail)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn renders_with_path_and_line() {
let finding = Finding::on_line(
RuleId::BugzillaReportBodyFitsReportWidth,
"_docs/reference/known-issues/KI-vendor-500.md",
12,
"83 columns",
);
assert_eq!(
finding.to_string(),
"FAIL known-issues:a-bugzilla-report-body-fits-in-79-columns \
_docs/reference/known-issues/KI-vendor-500.md:12: 83 columns"
);
}
#[test]
fn renders_without_a_path() {
let finding = Finding::global(
RuleId::GateMessageCitesTheRule,
"x:y resolves to no requirement",
);
assert_eq!(
finding.to_string(),
"FAIL spec-to-code:a-gate-message-cites-the-rule: x:y resolves to no requirement"
);
}
}