driven/validation/
conflicts.rs1use super::Conflict;
4use crate::{Result, parser::UnifiedRule};
5
6#[derive(Debug, Default)]
8pub struct ConflictDetector;
9
10impl ConflictDetector {
11 pub fn new() -> Self {
13 Self
14 }
15
16 pub fn detect(&self, rules: &[UnifiedRule]) -> Result<Vec<Conflict>> {
18 let mut conflicts = Vec::new();
19
20 conflicts.extend(self.check_naming_conflicts(rules));
22
23 conflicts.extend(self.check_contradictions(rules));
25
26 Ok(conflicts)
27 }
28
29 fn check_naming_conflicts(&self, rules: &[UnifiedRule]) -> Vec<Conflict> {
30 let mut conflicts = Vec::new();
31 let mut naming_rules: Vec<&str> = Vec::new();
32
33 for rule in rules {
34 if let UnifiedRule::Standard { description, .. } = rule {
35 let lower = description.to_lowercase();
36 if lower.contains("snake_case")
37 || lower.contains("camelcase")
38 || lower.contains("pascalcase")
39 {
40 naming_rules.push(description);
41 }
42 }
43 }
44
45 if naming_rules.len() > 1 {
47 let has_snake = naming_rules
48 .iter()
49 .any(|r| r.to_lowercase().contains("snake_case"));
50 let has_camel = naming_rules
51 .iter()
52 .any(|r| r.to_lowercase().contains("camelcase"));
53
54 if has_snake && has_camel {
55 let func_snake = naming_rules.iter().any(|r| {
57 r.to_lowercase().contains("function") && r.to_lowercase().contains("snake_case")
58 });
59 let func_camel = naming_rules.iter().any(|r| {
60 r.to_lowercase().contains("function") && r.to_lowercase().contains("camelcase")
61 });
62
63 if func_snake && func_camel {
64 conflicts.push(Conflict {
65 description: "Conflicting function naming conventions".to_string(),
66 rules: naming_rules.iter().map(|s| s.to_string()).collect(),
67 suggestion: Some("Choose one naming convention for functions".to_string()),
68 });
69 }
70 }
71 }
72
73 conflicts
74 }
75
76 fn check_contradictions(&self, rules: &[UnifiedRule]) -> Vec<Conflict> {
77 let mut conflicts = Vec::new();
78
79 let contradiction_pairs = [
81 ("always", "never"),
82 ("must", "must not"),
83 ("require", "avoid"),
84 ];
85
86 let standards: Vec<_> = rules
87 .iter()
88 .filter_map(|r| {
89 if let UnifiedRule::Standard { description, .. } = r {
90 Some(description.as_str())
91 } else {
92 None
93 }
94 })
95 .collect();
96
97 for (positive, negative) in contradiction_pairs {
98 for &rule1 in &standards {
99 for &rule2 in &standards {
100 if rule1 == rule2 {
101 continue;
102 }
103
104 let r1_lower = rule1.to_lowercase();
105 let r2_lower = rule2.to_lowercase();
106
107 if (r1_lower.contains(positive) && r2_lower.contains(negative))
109 || (r1_lower.contains(negative) && r2_lower.contains(positive))
110 {
111 let r1_words: std::collections::HashSet<_> = r1_lower
113 .split_whitespace()
114 .filter(|w| w.len() > 4)
115 .collect();
116 let r2_words: std::collections::HashSet<_> = r2_lower
117 .split_whitespace()
118 .filter(|w| w.len() > 4)
119 .collect();
120
121 let overlap: Vec<_> = r1_words.intersection(&r2_words).collect();
122 if overlap.len() >= 2 {
123 conflicts.push(Conflict {
124 description: "Potentially contradicting rules".to_string(),
125 rules: vec![rule1.to_string(), rule2.to_string()],
126 suggestion: Some("Review these rules for consistency".to_string()),
127 });
128 }
129 }
130 }
131 }
132 }
133
134 conflicts
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn test_detector_new() {
144 let detector = ConflictDetector::new();
145 let conflicts = detector.detect(&[]).unwrap();
146 assert!(conflicts.is_empty());
147 }
148}