use crate::parser::UnifiedRule;
#[derive(Debug, Default)]
pub struct RuleDiffer;
impl RuleDiffer {
pub fn new() -> Self {
Self
}
pub fn diff(&self, old: &[UnifiedRule], new: &[UnifiedRule]) -> RuleDiff {
let mut diff = RuleDiff::default();
for new_rule in new {
if !self.contains_similar(old, new_rule) {
diff.added.push(new_rule.clone());
}
}
for old_rule in old {
if !self.contains_similar(new, old_rule) {
diff.removed.push(old_rule.clone());
}
}
diff
}
fn contains_similar(&self, rules: &[UnifiedRule], target: &UnifiedRule) -> bool {
rules.iter().any(|r| self.rules_similar(r, target))
}
fn rules_similar(&self, a: &UnifiedRule, b: &UnifiedRule) -> bool {
match (a, b) {
(
UnifiedRule::Standard {
description: desc_a,
..
},
UnifiedRule::Standard {
description: desc_b,
..
},
) => desc_a == desc_b,
(
UnifiedRule::Persona { name: name_a, .. },
UnifiedRule::Persona { name: name_b, .. },
) => name_a == name_b,
(UnifiedRule::Raw { content: a }, UnifiedRule::Raw { content: b }) => a == b,
_ => false,
}
}
}
#[derive(Debug, Default, Clone)]
pub struct RuleDiff {
pub added: Vec<UnifiedRule>,
pub removed: Vec<UnifiedRule>,
}
impl RuleDiff {
pub fn has_changes(&self) -> bool {
!self.added.is_empty() || !self.removed.is_empty()
}
pub fn change_count(&self) -> usize {
self.added.len() + self.removed.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::format::RuleCategory;
#[test]
fn test_diff_identical() {
let differ = RuleDiffer::new();
let rules = vec![UnifiedRule::Standard {
category: RuleCategory::Style,
priority: 0,
description: "Test rule".to_string(),
pattern: None,
}];
let diff = differ.diff(&rules, &rules);
assert!(!diff.has_changes());
}
#[test]
fn test_diff_added() {
let differ = RuleDiffer::new();
let old = vec![];
let new = vec![UnifiedRule::Standard {
category: RuleCategory::Style,
priority: 0,
description: "New rule".to_string(),
pattern: None,
}];
let diff = differ.diff(&old, &new);
assert!(diff.has_changes());
assert_eq!(diff.added.len(), 1);
assert_eq!(diff.removed.len(), 0);
}
#[test]
fn test_diff_removed() {
let differ = RuleDiffer::new();
let old = vec![UnifiedRule::Standard {
category: RuleCategory::Style,
priority: 0,
description: "Old rule".to_string(),
pattern: None,
}];
let new = vec![];
let diff = differ.diff(&old, &new);
assert!(diff.has_changes());
assert_eq!(diff.added.len(), 0);
assert_eq!(diff.removed.len(), 1);
}
}