use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::diagnostics::codes::ValidationCode;
use crate::{Severity, ValidationReport};
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum RuleSeverity {
Off,
Info,
Warn,
Error,
Critical,
}
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RulesConfig(HashMap<String, RuleSeverity>);
impl RulesConfig {
pub fn set(&mut self, code: impl ValidationCode, severity: RuleSeverity) {
self.0.insert(code.code().to_string(), severity);
}
pub fn set_raw(&mut self, key: String, severity: RuleSeverity) {
self.0.insert(key, severity);
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &RuleSeverity)> {
self.0.iter()
}
}
fn rule_matches(code: &str, key: &str) -> bool {
code == key || code.rsplit('/').next() == Some(key)
}
impl ValidationReport {
pub fn apply_rules(mut self, rules: &RulesConfig) -> Self {
if rules.is_empty() {
return self;
}
let all: Vec<_> = self
.critical
.drain(..)
.chain(self.errors.drain(..))
.chain(self.warnings.drain(..))
.chain(self.info.drain(..))
.collect();
for mut issue in all {
let override_sev = rules
.iter()
.find(|(k, _)| rule_matches(&issue.code, k))
.map(|(_, v)| v);
match override_sev {
Some(RuleSeverity::Off) => {} Some(RuleSeverity::Info) => {
issue.severity = Severity::Info;
self.info.push(issue);
}
Some(RuleSeverity::Warn) => {
issue.severity = Severity::Warning;
self.warnings.push(issue);
}
Some(RuleSeverity::Error) => {
issue.severity = Severity::Error;
self.errors.push(issue);
}
Some(RuleSeverity::Critical) => {
issue.severity = Severity::Critical;
self.critical.push(issue);
}
None => match issue.severity {
Severity::Critical => self.critical.push(issue),
Severity::Error => self.errors.push(issue),
Severity::Warning => self.warnings.push(issue),
Severity::Info => self.info.push(issue),
},
}
}
self.is_playable = self.critical.is_empty();
self.is_compliant = self.critical.is_empty() && self.errors.is_empty();
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rules_config_accessors() {
let mut rules = RulesConfig::default();
assert!(rules.is_empty());
assert_eq!(rules.len(), 0);
rules.set(
crate::assetmap::codes::St2067_2_2020::FileNotFound,
RuleSeverity::Critical,
);
assert!(!rules.is_empty());
assert_eq!(rules.len(), 1);
assert_eq!(rules.iter().count(), 1);
}
#[test]
fn rules_config_serde_round_trip() {
let mut rules = RulesConfig::default();
rules.set(
crate::assetmap::codes::St2067_2_2020::FileNotFound,
RuleSeverity::Off,
);
let json = serde_json::to_string(&rules).unwrap();
let deserialized: RulesConfig = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.len(), 1);
}
}