Skip to main content

pedant_types/
diff.rs

1use rustc_hash::FxHashSet;
2use serde::{Deserialize, Serialize};
3
4use crate::{Capability, CapabilityFinding, CapabilityProfile};
5
6/// Set-difference between two capability profiles.
7#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
8pub struct CapabilityDiff {
9    /// Findings present in `new` but absent from `old`.
10    pub added: Box<[CapabilityFinding]>,
11    /// Findings present in `old` but absent from `new`.
12    pub removed: Box<[CapabilityFinding]>,
13    /// Capability types that gained their first finding.
14    pub new_capabilities: Box<[Capability]>,
15    /// Capability types that lost their last finding.
16    pub dropped_capabilities: Box<[Capability]>,
17}
18
19impl CapabilityDiff {
20    /// No findings were added, removed, or changed capability type.
21    pub fn is_empty(&self) -> bool {
22        self.added.is_empty()
23            && self.removed.is_empty()
24            && self.new_capabilities.is_empty()
25            && self.dropped_capabilities.is_empty()
26    }
27
28    /// Produce the set-difference between two profiles.
29    pub fn compute(old: &CapabilityProfile, new: &CapabilityProfile) -> Self {
30        let old_set: FxHashSet<&CapabilityFinding> = old.findings.iter().collect();
31        let new_set: FxHashSet<&CapabilityFinding> = new.findings.iter().collect();
32
33        let added: Box<[CapabilityFinding]> = new
34            .findings
35            .iter()
36            .filter(|f| !old_set.contains(f))
37            .cloned()
38            .collect();
39
40        let removed: Box<[CapabilityFinding]> = old
41            .findings
42            .iter()
43            .filter(|f| !new_set.contains(f))
44            .cloned()
45            .collect();
46
47        let old_caps: FxHashSet<Capability> = old.findings.iter().map(|f| f.capability).collect();
48        let new_caps: FxHashSet<Capability> = new.findings.iter().map(|f| f.capability).collect();
49
50        let mut new_capabilities: Vec<Capability> =
51            new_caps.difference(&old_caps).copied().collect();
52        new_capabilities.sort_unstable();
53
54        let mut dropped_capabilities: Vec<Capability> =
55            old_caps.difference(&new_caps).copied().collect();
56        dropped_capabilities.sort_unstable();
57
58        Self {
59            added,
60            removed,
61            new_capabilities: new_capabilities.into_boxed_slice(),
62            dropped_capabilities: dropped_capabilities.into_boxed_slice(),
63        }
64    }
65}