sbom_tools/diff/changes/
licenses.rs1use crate::diff::LicenseChange;
4use crate::diff::traits::{ChangeComputer, ComponentMatches, LicenseChangeSet};
5use crate::model::NormalizedSbom;
6use std::collections::HashMap;
7
8pub struct LicenseChangeComputer;
10
11impl LicenseChangeComputer {
12 #[must_use]
14 pub const fn new() -> Self {
15 Self
16 }
17}
18
19impl Default for LicenseChangeComputer {
20 fn default() -> Self {
21 Self::new()
22 }
23}
24
25impl ChangeComputer for LicenseChangeComputer {
26 type ChangeSet = LicenseChangeSet;
27
28 fn compute(
29 &self,
30 old: &NormalizedSbom,
31 new: &NormalizedSbom,
32 _matches: &ComponentMatches,
33 ) -> LicenseChangeSet {
34 let mut result = LicenseChangeSet::new();
35
36 let mut old_licenses: HashMap<String, Vec<String>> = HashMap::new();
38 for (_id, comp) in &old.components {
39 for lic in &comp.licenses.declared {
40 old_licenses
41 .entry(lic.expression.clone())
42 .or_default()
43 .push(comp.name.clone());
44 }
45 }
46
47 let mut new_licenses: HashMap<String, Vec<String>> = HashMap::new();
49 for (_id, comp) in &new.components {
50 for lic in &comp.licenses.declared {
51 new_licenses
52 .entry(lic.expression.clone())
53 .or_default()
54 .push(comp.name.clone());
55 }
56 }
57
58 for (license, components) in &new_licenses {
60 if !old_licenses.contains_key(license) {
61 result.new_licenses.push(LicenseChange {
62 license: license.clone(),
63 components: components.clone(),
64 family: "Unknown".to_string(), });
66 }
67 }
68
69 for (license, components) in &old_licenses {
71 if !new_licenses.contains_key(license) {
72 result.removed_licenses.push(LicenseChange {
73 license: license.clone(),
74 components: components.clone(),
75 family: "Unknown".to_string(),
76 });
77 }
78 }
79
80 result
81 }
82
83 fn name(&self) -> &'static str {
84 "LicenseChangeComputer"
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_license_change_computer_default() {
94 let computer = LicenseChangeComputer;
95 assert_eq!(computer.name(), "LicenseChangeComputer");
96 }
97
98 #[test]
99 fn test_empty_sboms() {
100 let computer = LicenseChangeComputer;
101 let old = NormalizedSbom::default();
102 let new = NormalizedSbom::default();
103 let matches = ComponentMatches::new();
104
105 let result = computer.compute(&old, &new, &matches);
106 assert!(result.is_empty());
107 }
108}