use std::path::Path;
use flower_core::Seg;
use prov::prov_graph::field::Step;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Site {
Meta(Vec<Seg>),
Body(std::ops::Range<usize>),
Document,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Severity {
Error,
Warning,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Finding {
pub site: Site,
pub severity: Severity,
pub message: String,
pub kind: &'static str,
}
pub fn place(finding: &prov::Finding, subject: &Path) -> Finding {
Finding {
site: site_of(finding),
severity: match finding.severity() {
prov::Severity::Warning => Severity::Warning,
prov::Severity::Error => Severity::Error,
},
message: trim_subject(&finding.to_string(), subject),
kind: finding.kind(),
}
}
pub fn site_of(finding: &prov::Finding) -> Site {
let Some(site) = link_site(finding) else {
return field_site(finding);
};
match site {
prov::LinkSite::Body(span) => Site::Body(span.clone()),
prov::LinkSite::Relation { field, index } => {
let mut path = vec![Seg::Key(field.clone())];
if let Some(i) = index {
path.push(Seg::Index(*i));
}
Site::Meta(path)
}
prov::LinkSite::Field { .. } => match site.address() {
Some(address) => Site::Meta(
address
.steps()
.iter()
.map(|step| match step {
Step::Key(key) => Seg::Key(key.clone()),
Step::At(i) => Seg::Index(*i),
Step::Each => unreachable!("an address has no `[]` step"),
})
.collect(),
),
None => Site::Document,
},
}
}
fn link_site(finding: &prov::Finding) -> Option<&prov::LinkSite> {
use prov::Finding as F;
match finding {
F::BrokenLink { site, .. }
| F::CaseMismatch { site, .. }
| F::MalformedId { site, .. }
| F::StaleLabel { site, .. }
| F::DanglingId { site, .. }
| F::AmbiguousAlias { site, .. } => Some(site),
_ => None,
}
}
fn field_site(finding: &prov::Finding) -> Site {
use prov::Finding as F;
match finding {
F::UnknownTerm { field, .. } | F::TermNearMiss { field, .. } => {
Site::Meta(vec![Seg::Key(field.clone())])
}
F::FieldScopeUnresolved { field, .. } => Site::Meta(vec![Seg::Key(field.clone())]),
_ => Site::Document,
}
}
fn trim_subject(message: &str, subject: &Path) -> String {
let prefix = format!("{}: ", subject.display());
message.strip_prefix(&prefix).unwrap_or(message).to_string()
}