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