use crate::core::model::Model;
use crate::io::threemf_package::ThreemfPackage;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Severity {
Error,
Warning,
Info,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ValidationIssue {
pub severity: Severity,
pub message: String,
}
impl ValidationIssue {
pub fn new(severity: Severity, message: impl Into<String>) -> Self {
Self {
severity,
message: message.into(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ValidationResult {
pub issues: Vec<ValidationIssue>,
pub is_valid: bool,
}
impl ValidationResult {
pub fn new(issues: Vec<ValidationIssue>) -> Self {
let is_valid = !issues.iter().any(|issue| issue.severity == Severity::Error);
Self { issues, is_valid }
}
pub fn valid() -> Self {
Self {
issues: Vec::new(),
is_valid: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValidationScope {
ModelOrPackage,
PackageOnly,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValidationRule {
ObjectIdReference,
ResourceIdReference,
BuildItemReference,
ComponentReference,
}
impl ValidationRule {
pub fn scope(&self) -> ValidationScope {
match self {
ValidationRule::ObjectIdReference => ValidationScope::ModelOrPackage,
ValidationRule::ResourceIdReference => ValidationScope::ModelOrPackage,
ValidationRule::BuildItemReference => ValidationScope::ModelOrPackage,
ValidationRule::ComponentReference => ValidationScope::ModelOrPackage,
}
}
}
#[derive(Debug, Clone)]
pub struct Validator {
rules: Vec<ValidationRule>,
}
impl Default for Validator {
fn default() -> Self {
Self::new()
}
}
impl Validator {
pub fn new() -> Self {
Self { rules: Vec::new() }
}
pub fn with_rule(mut self, rule: ValidationRule) -> Self {
self.rules.push(rule);
self
}
pub fn validate_model(&self, model: &Model) -> ValidationResult {
let mut issues = Vec::new();
for rule in &self.rules {
if rule.scope() == ValidationScope::ModelOrPackage {
let rule_issues = crate::io::validator_rules::run_rule_for_model(rule, model);
issues.extend(rule_issues);
}
}
ValidationResult::new(issues)
}
pub fn validate_package(&self, package: &ThreemfPackage) -> ValidationResult {
let mut issues = Vec::new();
for rule in &self.rules {
let rule_issues = crate::io::validator_rules::run_rule_for_package(rule, package);
issues.extend(rule_issues);
}
ValidationResult::new(issues)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validation_result_valid() {
let result = ValidationResult::valid();
assert!(result.is_valid);
assert!(result.issues.is_empty());
}
#[test]
fn test_validation_result_with_warning() {
let issues = vec![ValidationIssue::new(Severity::Warning, "Test warning")];
let result = ValidationResult::new(issues);
assert!(result.is_valid);
assert_eq!(result.issues.len(), 1);
}
#[test]
fn test_validation_result_with_error() {
let issues = vec![ValidationIssue::new(Severity::Error, "Test error")];
let result = ValidationResult::new(issues);
assert!(!result.is_valid);
assert_eq!(result.issues.len(), 1);
}
#[test]
fn test_validation_result_mixed() {
let issues = vec![
ValidationIssue::new(Severity::Info, "Test info"),
ValidationIssue::new(Severity::Warning, "Test warning"),
ValidationIssue::new(Severity::Error, "Test error"),
];
let result = ValidationResult::new(issues);
assert!(!result.is_valid);
assert_eq!(result.issues.len(), 3);
}
#[test]
fn test_validator_builder_pattern() {
let validator = Validator::new()
.with_rule(ValidationRule::ObjectIdReference)
.with_rule(ValidationRule::ResourceIdReference);
assert_eq!(validator.rules.len(), 2);
assert!(validator.rules.contains(&ValidationRule::ObjectIdReference));
assert!(
validator
.rules
.contains(&ValidationRule::ResourceIdReference)
);
}
#[test]
fn test_rule_scopes() {
assert_eq!(
ValidationRule::ObjectIdReference.scope(),
ValidationScope::ModelOrPackage
);
assert_eq!(
ValidationRule::ResourceIdReference.scope(),
ValidationScope::ModelOrPackage
);
}
}