Skip to main content

driven/sync/
differ.rs

1//! Rule difference calculator
2
3use crate::parser::UnifiedRule;
4
5/// Calculates differences between rule sets
6#[derive(Debug, Default)]
7pub struct RuleDiffer;
8
9impl RuleDiffer {
10    /// Create a new differ
11    pub fn new() -> Self {
12        Self
13    }
14
15    /// Calculate differences between two rule sets
16    pub fn diff(&self, old: &[UnifiedRule], new: &[UnifiedRule]) -> RuleDiff {
17        let mut diff = RuleDiff::default();
18
19        // Simple comparison based on type and content
20        for new_rule in new {
21            if !self.contains_similar(old, new_rule) {
22                diff.added.push(new_rule.clone());
23            }
24        }
25
26        for old_rule in old {
27            if !self.contains_similar(new, old_rule) {
28                diff.removed.push(old_rule.clone());
29            }
30        }
31
32        diff
33    }
34
35    fn contains_similar(&self, rules: &[UnifiedRule], target: &UnifiedRule) -> bool {
36        rules.iter().any(|r| self.rules_similar(r, target))
37    }
38
39    fn rules_similar(&self, a: &UnifiedRule, b: &UnifiedRule) -> bool {
40        match (a, b) {
41            (
42                UnifiedRule::Standard {
43                    description: desc_a,
44                    ..
45                },
46                UnifiedRule::Standard {
47                    description: desc_b,
48                    ..
49                },
50            ) => desc_a == desc_b,
51            (
52                UnifiedRule::Persona { name: name_a, .. },
53                UnifiedRule::Persona { name: name_b, .. },
54            ) => name_a == name_b,
55            (UnifiedRule::Raw { content: a }, UnifiedRule::Raw { content: b }) => a == b,
56            _ => false,
57        }
58    }
59}
60
61/// Difference between two rule sets
62#[derive(Debug, Default, Clone)]
63pub struct RuleDiff {
64    /// Rules added in new set
65    pub added: Vec<UnifiedRule>,
66    /// Rules removed from old set
67    pub removed: Vec<UnifiedRule>,
68}
69
70impl RuleDiff {
71    /// Check if there are any differences
72    pub fn has_changes(&self) -> bool {
73        !self.added.is_empty() || !self.removed.is_empty()
74    }
75
76    /// Get total number of changes
77    pub fn change_count(&self) -> usize {
78        self.added.len() + self.removed.len()
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use crate::format::RuleCategory;
86
87    #[test]
88    fn test_diff_identical() {
89        let differ = RuleDiffer::new();
90        let rules = vec![UnifiedRule::Standard {
91            category: RuleCategory::Style,
92            priority: 0,
93            description: "Test rule".to_string(),
94            pattern: None,
95        }];
96
97        let diff = differ.diff(&rules, &rules);
98        assert!(!diff.has_changes());
99    }
100
101    #[test]
102    fn test_diff_added() {
103        let differ = RuleDiffer::new();
104        let old = vec![];
105        let new = vec![UnifiedRule::Standard {
106            category: RuleCategory::Style,
107            priority: 0,
108            description: "New rule".to_string(),
109            pattern: None,
110        }];
111
112        let diff = differ.diff(&old, &new);
113        assert!(diff.has_changes());
114        assert_eq!(diff.added.len(), 1);
115        assert_eq!(diff.removed.len(), 0);
116    }
117
118    #[test]
119    fn test_diff_removed() {
120        let differ = RuleDiffer::new();
121        let old = vec![UnifiedRule::Standard {
122            category: RuleCategory::Style,
123            priority: 0,
124            description: "Old rule".to_string(),
125            pattern: None,
126        }];
127        let new = vec![];
128
129        let diff = differ.diff(&old, &new);
130        assert!(diff.has_changes());
131        assert_eq!(diff.added.len(), 0);
132        assert_eq!(diff.removed.len(), 1);
133    }
134}