whichlicense_classification/
lib.rs1pub mod classification {
19 use std::collections::HashMap;
20
21 use serde::{Deserialize, Serialize};
22
23 #[derive(Debug, Clone, PartialEq, Eq)]
24 pub enum CompliancyStatus {
25 Compliant,
26 NonCompliant(Vec<CompatibilityEntry>),
28
29 UnknownLeading,
31
32 Unknown(Vec<String>),
34 }
35
36 #[derive(Debug, Deserialize, Serialize, Copy, Clone, PartialEq, Eq)]
37 pub enum CompatibilityStatus {
38 Compatible,
39 Incompatible,
40 Unknown,
41 }
42
43 #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
44 pub struct CompatibilityEntry {
45 pub name: String,
46 pub compatible: CompatibilityStatus,
47 pub explanation: String,
48 }
49
50 impl CompatibilityEntry {
51 pub fn new_unknown(key: &str) -> CompatibilityEntry {
52 CompatibilityEntry {
53 name: key.to_string(),
54 compatible: CompatibilityStatus::Unknown,
55 explanation: format!("No compliancy data found for {}", key),
56 }
57 }
58 }
59
60 #[derive(Debug, Deserialize, Serialize, Clone)]
61 pub struct LicenseEntry {
62 pub name: String,
63 pub compatibility: HashMap<String, CompatibilityEntry>,
64 pub spdx_license_key: Option<String>,
65 }
66
67 impl LicenseEntry {
68 pub fn check_compatibility(&self, other: &LicenseEntry) -> CompatibilityStatus {
69 if let Some(compatibility) = self.compatibility.get(&other.name) {
70 compatibility.compatible
71 } else {
72 CompatibilityStatus::Unknown
73 }
74 }
75
76 pub fn get_all(&self, keys: &Vec<&str>) -> Vec<Option<&CompatibilityEntry>> {
77 keys.iter()
78 .map(|key| self.compatibility.get(*key))
79 .collect()
80 }
81 }
82
83 #[derive(Debug, Deserialize, Serialize, Clone)]
84 pub struct CompatibilityIndex {
85 pub data: HashMap<String, LicenseEntry>,
86 }
87
88 impl CompatibilityIndex {
89 pub fn new() -> CompatibilityIndex {
90 CompatibilityIndex {
91 data: HashMap::new(),
92 }
93 }
94
95 pub fn add(&mut self, key: &str, classification: LicenseEntry) {
96 self.data.insert(key.to_owned(), classification);
97 }
98
99 pub fn get(&self, key: &str) -> Option<&LicenseEntry> {
100 self.data.get(key)
101 }
102
103 pub fn get_all(&self, keys: &Vec<&str>) -> Vec<Option<&LicenseEntry>> {
104 keys.iter().map(|key| self.get(key)).collect()
105 }
106
107 pub fn load_from_memory(&mut self, raw: &[u8]) {
108 self.data = bincode::deserialize(&raw[..]).unwrap();
109 }
110
111 pub fn from_memory(raw: &[u8]) -> CompatibilityIndex {
112 let mut classifier = CompatibilityIndex::new();
113 classifier.load_from_memory(raw);
114 classifier
115 }
116
117 pub fn load_from_file(&mut self, path: &str) {
118 let raw = std::fs::read(path).unwrap();
119 self.load_from_memory(&raw);
120 }
121
122 pub fn from_file(path: &str) -> CompatibilityIndex {
123 let mut classifier = CompatibilityIndex::new();
124 classifier.load_from_file(path);
125 classifier
126 }
127
128 pub fn save_to_file(&self, path: &str) {
129 let raw = bincode::serialize(&self.data).unwrap();
130 std::fs::write(path, raw).unwrap();
131 }
132
133 pub fn check_compliancy(
134 &self,
135 leading_license: &str,
136 subordinate_licenses: &Vec<&str>,
137 ) -> CompliancyStatus {
138 let host_classification = self.get(leading_license);
139 if host_classification.is_none() {
140 return CompliancyStatus::UnknownLeading;
141 }
142
143 let slimmed_matrix: Vec<CompatibilityEntry> = host_classification
144 .unwrap()
145 .get_all(subordinate_licenses)
146 .iter()
147 .map(|c| {
148 if let Some(c) = c {
149 c.clone().clone()
150 } else {
151 CompatibilityEntry::new_unknown(leading_license)
152 }
153 })
154 .collect();
155
156 if slimmed_matrix
157 .iter()
158 .any(|classification| classification.compatible == CompatibilityStatus::Unknown)
159 {
160 CompliancyStatus::Unknown(
161 slimmed_matrix
162 .iter()
163 .filter(|classification| {
164 classification.compatible == CompatibilityStatus::Unknown
165 })
166 .map(|classification| classification.name.to_owned())
167 .collect(),
168 )
169 } else if slimmed_matrix.iter().any(|classification| {
170 classification.compatible == CompatibilityStatus::Incompatible
171 }) {
172 CompliancyStatus::NonCompliant(
173 slimmed_matrix
174 .iter()
175 .filter(|classification| {
176 classification.compatible == CompatibilityStatus::Incompatible
177 })
178 .map(|classification| classification.clone())
179 .collect(),
180 )
181 } else {
182 CompliancyStatus::Compliant
183 }
184 }
185 }
186}