mod completeness;
mod conflicts;
mod linter;
pub use completeness::CoverageAnalyzer;
pub use conflicts::ConflictDetector;
pub use linter::{LintResult, LintSeverity, Linter};
use crate::{Result, parser::UnifiedRule};
#[derive(Debug, Default)]
pub struct ValidationResult {
pub lint_issues: Vec<LintResult>,
pub conflicts: Vec<Conflict>,
pub coverage_gaps: Vec<String>,
}
impl ValidationResult {
pub fn is_valid(&self) -> bool {
!self
.lint_issues
.iter()
.any(|i| matches!(i.severity, LintSeverity::Error))
&& self.conflicts.is_empty()
}
pub fn error_count(&self) -> usize {
self.lint_issues
.iter()
.filter(|i| matches!(i.severity, LintSeverity::Error))
.count()
+ self.conflicts.len()
}
pub fn warning_count(&self) -> usize {
self.lint_issues
.iter()
.filter(|i| matches!(i.severity, LintSeverity::Warning))
.count()
}
}
#[derive(Debug, Clone)]
pub struct Conflict {
pub description: String,
pub rules: Vec<String>,
pub suggestion: Option<String>,
}
pub fn validate(rules: &[UnifiedRule]) -> Result<ValidationResult> {
let mut result = ValidationResult::default();
let linter = Linter::new();
result.lint_issues = linter.lint(rules)?;
let detector = ConflictDetector::new();
result.conflicts = detector.detect(rules)?;
let analyzer = CoverageAnalyzer::new();
result.coverage_gaps = analyzer.analyze(rules)?;
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validation_result_default() {
let result = ValidationResult::default();
assert!(result.is_valid());
assert_eq!(result.error_count(), 0);
}
#[test]
fn test_validate_empty() {
let result = validate(&[]).unwrap();
assert!(result.is_valid());
}
}