Skip to main content

pedant_types/
diff.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{Capability, CapabilityFinding, CapabilityProfile};
4
5/// The difference between two capability profiles.
6#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
7pub struct CapabilityDiff {
8    /// Findings present in `new` but not in `old`.
9    pub added: Vec<CapabilityFinding>,
10    /// Findings present in `old` but not in `new`.
11    pub removed: Vec<CapabilityFinding>,
12    /// Capabilities that appear in `new` but had zero findings in `old`.
13    pub new_capabilities: Vec<Capability>,
14    /// Capabilities that appeared in `old` but have zero findings in `new`.
15    pub dropped_capabilities: Vec<Capability>,
16}
17
18impl CapabilityDiff {
19    /// Compute the diff between an old and new profile.
20    pub fn compute(old: &CapabilityProfile, new: &CapabilityProfile) -> Self {
21        let added: Vec<CapabilityFinding> = new
22            .findings
23            .iter()
24            .filter(|f| !old.findings.contains(f))
25            .cloned()
26            .collect();
27
28        let removed: Vec<CapabilityFinding> = old
29            .findings
30            .iter()
31            .filter(|f| !new.findings.contains(f))
32            .cloned()
33            .collect();
34
35        let old_caps = old.capabilities();
36        let new_caps = new.capabilities();
37
38        let new_capabilities: Vec<Capability> = new_caps
39            .iter()
40            .filter(|c| !old_caps.contains(c))
41            .copied()
42            .collect();
43
44        let dropped_capabilities: Vec<Capability> = old_caps
45            .iter()
46            .filter(|c| !new_caps.contains(c))
47            .copied()
48            .collect();
49
50        Self {
51            added,
52            removed,
53            new_capabilities,
54            dropped_capabilities,
55        }
56    }
57}