use crate::{Result, format::RuleCategory, parser::UnifiedRule};
#[derive(Debug, Default)]
pub struct CoverageAnalyzer;
impl CoverageAnalyzer {
pub fn new() -> Self {
Self
}
pub fn analyze(&self, rules: &[UnifiedRule]) -> Result<Vec<String>> {
let mut gaps = Vec::new();
let covered_categories = self.get_covered_categories(rules);
let recommended = [
RuleCategory::Style,
RuleCategory::Naming,
RuleCategory::ErrorHandling,
RuleCategory::Testing,
RuleCategory::Documentation,
];
for cat in recommended {
if !covered_categories.contains(&cat) {
gaps.push(format!("No rules for category: {:?}", cat));
}
}
let has_persona = rules
.iter()
.any(|r| matches!(r, UnifiedRule::Persona { .. }));
if !has_persona {
gaps.push("No AI persona defined".to_string());
}
let has_context = rules
.iter()
.any(|r| matches!(r, UnifiedRule::Context { .. }));
if !has_context {
gaps.push("No project context defined".to_string());
}
Ok(gaps)
}
fn get_covered_categories(
&self,
rules: &[UnifiedRule],
) -> std::collections::HashSet<RuleCategory> {
rules
.iter()
.filter_map(|r| {
if let UnifiedRule::Standard { category, .. } = r {
Some(*category)
} else {
None
}
})
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_analyzer_empty() {
let analyzer = CoverageAnalyzer::new();
let gaps = analyzer.analyze(&[]).unwrap();
assert!(!gaps.is_empty());
}
#[test]
fn test_analyzer_complete() {
let analyzer = CoverageAnalyzer::new();
let rules = vec![
UnifiedRule::Persona {
name: "Test".to_string(),
role: "Tester".to_string(),
identity: None,
style: None,
traits: vec![],
principles: vec![],
},
UnifiedRule::Context {
includes: vec![],
excludes: vec![],
focus: vec![],
},
UnifiedRule::Standard {
category: RuleCategory::Style,
priority: 0,
description: "Style rule".to_string(),
pattern: None,
},
UnifiedRule::Standard {
category: RuleCategory::Naming,
priority: 0,
description: "Naming rule".to_string(),
pattern: None,
},
UnifiedRule::Standard {
category: RuleCategory::ErrorHandling,
priority: 0,
description: "Error rule".to_string(),
pattern: None,
},
UnifiedRule::Standard {
category: RuleCategory::Testing,
priority: 0,
description: "Testing rule".to_string(),
pattern: None,
},
UnifiedRule::Standard {
category: RuleCategory::Documentation,
priority: 0,
description: "Doc rule".to_string(),
pattern: None,
},
];
let gaps = analyzer.analyze(&rules).unwrap();
assert!(gaps.is_empty());
}
}