Skip to main content

driven/validation/
mod.rs

1//! Rule Validation
2//!
3//! Linting, conflict detection, and coverage analysis for AI rules.
4
5mod 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/// Result of validating rules
16#[derive(Debug, Default)]
17pub struct ValidationResult {
18    /// Lint issues found
19    pub lint_issues: Vec<LintResult>,
20    /// Conflicts detected
21    pub conflicts: Vec<Conflict>,
22    /// Coverage gaps
23    pub coverage_gaps: Vec<String>,
24}
25
26impl ValidationResult {
27    /// Check if validation passed (no errors)
28    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    /// Get error count
37    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    /// Get warning count
46    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/// A conflict between rules
55#[derive(Debug, Clone)]
56pub struct Conflict {
57    /// Description of the conflict
58    pub description: String,
59    /// Rules involved
60    pub rules: Vec<String>,
61    /// Suggested resolution
62    pub suggestion: Option<String>,
63}
64
65/// Validate a set of rules
66pub fn validate(rules: &[UnifiedRule]) -> Result<ValidationResult> {
67    let mut result = ValidationResult::default();
68
69    // Run linter
70    let linter = Linter::new();
71    result.lint_issues = linter.lint(rules)?;
72
73    // Detect conflicts
74    let detector = ConflictDetector::new();
75    result.conflicts = detector.detect(rules)?;
76
77    // Analyze coverage
78    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}