1mod completeness;
6mod conflicts;
7mod linter;
8
9pub use completeness::CoverageAnalyzer;
10pub use conflicts::ConflictDetector;
11pub use linter::{LintResult, LintSeverity, Linter};
12
13use crate::{Result, parser::UnifiedRule};
14
15#[derive(Debug, Default)]
17pub struct ValidationResult {
18 pub lint_issues: Vec<LintResult>,
20 pub conflicts: Vec<Conflict>,
22 pub coverage_gaps: Vec<String>,
24}
25
26impl ValidationResult {
27 pub fn is_valid(&self) -> bool {
29 !self
30 .lint_issues
31 .iter()
32 .any(|i| matches!(i.severity, LintSeverity::Error))
33 && self.conflicts.is_empty()
34 }
35
36 pub fn error_count(&self) -> usize {
38 self.lint_issues
39 .iter()
40 .filter(|i| matches!(i.severity, LintSeverity::Error))
41 .count()
42 + self.conflicts.len()
43 }
44
45 pub fn warning_count(&self) -> usize {
47 self.lint_issues
48 .iter()
49 .filter(|i| matches!(i.severity, LintSeverity::Warning))
50 .count()
51 }
52}
53
54#[derive(Debug, Clone)]
56pub struct Conflict {
57 pub description: String,
59 pub rules: Vec<String>,
61 pub suggestion: Option<String>,
63}
64
65pub fn validate(rules: &[UnifiedRule]) -> Result<ValidationResult> {
67 let mut result = ValidationResult::default();
68
69 let linter = Linter::new();
71 result.lint_issues = linter.lint(rules)?;
72
73 let detector = ConflictDetector::new();
75 result.conflicts = detector.detect(rules)?;
76
77 let analyzer = CoverageAnalyzer::new();
79 result.coverage_gaps = analyzer.analyze(rules)?;
80
81 Ok(result)
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_validation_result_default() {
90 let result = ValidationResult::default();
91 assert!(result.is_valid());
92 assert_eq!(result.error_count(), 0);
93 }
94
95 #[test]
96 fn test_validate_empty() {
97 let result = validate(&[]).unwrap();
98 assert!(result.is_valid());
99 }
100}