use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ConstraintType {
Cardinality,
Datatype,
Enumeration,
Pattern,
StringLength,
}
impl std::fmt::Display for ConstraintType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConstraintType::Cardinality => write!(f, "Cardinality"),
ConstraintType::Datatype => write!(f, "Datatype"),
ConstraintType::Enumeration => write!(f, "Enumeration"),
ConstraintType::Pattern => write!(f, "Pattern"),
ConstraintType::StringLength => write!(f, "StringLength"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Severity {
Violation,
Warning,
Info,
}
impl std::fmt::Display for Severity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Severity::Violation => write!(f, "VIOLATION"),
Severity::Warning => write!(f, "WARNING"),
Severity::Info => write!(f, "INFO"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SourceLocation {
pub file_path: PathBuf,
pub line: usize,
pub column: usize,
}
#[derive(Debug, Clone)]
pub struct Violation {
pub focus_node: String,
pub result_path: Option<String>,
pub constraint_type: ConstraintType,
pub source_shape: String,
pub message: String,
pub location: Option<SourceLocation>,
pub severity: Severity,
pub expected_value: Option<String>,
pub actual_value: Option<String>,
}
impl Violation {
pub fn new(
focus_node: impl Into<String>, constraint_type: ConstraintType, message: impl Into<String>,
) -> Self {
Self {
focus_node: focus_node.into(),
result_path: None,
constraint_type,
source_shape: String::new(),
message: message.into(),
location: None,
severity: Severity::Violation,
expected_value: None,
actual_value: None,
}
}
pub fn with_result_path(mut self, path: impl Into<String>) -> Self {
self.result_path = Some(path.into());
self
}
pub fn with_source_shape(mut self, shape: impl Into<String>) -> Self {
self.source_shape = shape.into();
self
}
pub fn with_severity(mut self, severity: Severity) -> Self {
self.severity = severity;
self
}
pub fn with_values(mut self, expected: impl Into<String>, actual: impl Into<String>) -> Self {
self.expected_value = Some(expected.into());
self.actual_value = Some(actual.into());
self
}
}
#[derive(Debug, Clone)]
pub struct ValidationResult {
pub file_path: Option<PathBuf>,
pub passed: bool,
pub violation_count: usize,
pub violations: Vec<Violation>,
pub duration_ms: u64,
}
impl ValidationResult {
pub fn pass(duration_ms: u64) -> Self {
Self {
file_path: None,
passed: true,
violation_count: 0,
violations: Vec::new(),
duration_ms,
}
}
pub fn fail(violations: Vec<Violation>, duration_ms: u64) -> Self {
let violation_count = violations.len();
Self {
file_path: None,
passed: false,
violation_count,
violations,
duration_ms,
}
}
pub fn with_file_path(mut self, path: impl Into<PathBuf>) -> Self {
self.file_path = Some(path.into());
self
}
pub fn violations_by_severity(&self, severity: Severity) -> Vec<&Violation> {
self.violations
.iter()
.filter(|v| v.severity == severity)
.collect()
}
pub fn blocking_violations(&self) -> Vec<&Violation> {
self.violations_by_severity(Severity::Violation)
}
pub fn has_blocking_violations(&self) -> bool {
self.violations
.iter()
.any(|v| v.severity == Severity::Violation)
}
}