use std::collections::BTreeMap;
use std::fmt;
use sbol_ontology::Ontology;
use crate::validation::report::Severity;
use crate::validation::spec::validation_rule_statuses;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub enum TopologyCompleteness {
#[default]
Conservative,
RequireKnownForNucleicAcids,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum RuleOverride {
Suppress,
Severity(Severity),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnknownRule {
pub rule: String,
}
impl fmt::Display for UnknownRule {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"`{}` is not a recognized SBOL validation rule",
self.rule
)
}
}
impl std::error::Error for UnknownRule {}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct PolicyOptions {
pub hash_algorithm_registry: HashAlgorithmRegistry,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub enum HashAlgorithmRegistry {
#[default]
Conservative,
Strict,
Lenient,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct ValidationOptions {
pub topology_completeness: TopologyCompleteness,
pub policy: PolicyOptions,
pub(crate) overrides: BTreeMap<&'static str, RuleOverride>,
pub(crate) severity_floor: Option<Severity>,
pub(crate) severity_ceiling: Option<Severity>,
pub(crate) ontology_extensions: Vec<Ontology>,
}
impl ValidationOptions {
pub fn allow(self, rule: &str) -> Result<Self, UnknownRule> {
self.override_rule(rule, RuleOverride::Suppress)
}
pub fn deny(self, rule: &str) -> Result<Self, UnknownRule> {
self.override_rule(rule, RuleOverride::Severity(Severity::Error))
}
pub fn warn(self, rule: &str) -> Result<Self, UnknownRule> {
self.override_rule(rule, RuleOverride::Severity(Severity::Warning))
}
pub fn with_severity_floor(mut self, floor: Severity) -> Self {
self.severity_floor = Some(floor);
self
}
pub fn with_severity_ceiling(mut self, ceiling: Severity) -> Self {
self.severity_ceiling = Some(ceiling);
self
}
pub fn with_ontology_extension(mut self, extension: Ontology) -> Self {
self.ontology_extensions.push(extension);
self
}
pub(crate) fn take_ontology_extensions(&mut self) -> Vec<Ontology> {
std::mem::take(&mut self.ontology_extensions)
}
pub fn overrides(&self) -> impl Iterator<Item = (&'static str, RuleOverride)> + '_ {
self.overrides.iter().map(|(rule, ovr)| (*rule, *ovr))
}
pub fn severity_floor(&self) -> Option<Severity> {
self.severity_floor
}
pub fn severity_ceiling(&self) -> Option<Severity> {
self.severity_ceiling
}
fn override_rule(mut self, rule: &str, ovr: RuleOverride) -> Result<Self, UnknownRule> {
let canonical = canonical_rule_id(rule)?;
self.overrides.insert(canonical, ovr);
Ok(self)
}
pub(crate) fn resolved_severity(
&self,
rule: &'static str,
catalog_default: Severity,
) -> Option<Severity> {
let base = match self.overrides.get(rule).copied() {
Some(RuleOverride::Suppress) => return None,
Some(RuleOverride::Severity(severity)) => severity,
None => catalog_default,
};
let with_floor = match self.severity_floor {
Some(floor) if floor > base => floor,
_ => base,
};
let with_ceiling = match self.severity_ceiling {
Some(ceiling) if ceiling < with_floor => ceiling,
_ => with_floor,
};
Some(with_ceiling)
}
}
fn canonical_rule_id(rule: &str) -> Result<&'static str, UnknownRule> {
validation_rule_statuses()
.iter()
.find(|status| status.rule == rule)
.map(|status| status.rule)
.ok_or_else(|| UnknownRule {
rule: rule.to_owned(),
})
}