1 | // SPDX-FileCopyrightText: 2022 HH Partners |
2 | // |
3 | // SPDX-License-Identifier: MIT |
4 | |
5 | //! Private inner structs for [`crate::SpdxExpression`]. |
6 | |
7 | use std::{collections::HashSet, fmt::Display}; |
8 | |
9 | use nom::Finish; |
10 | use serde::{de::Visitor, Deserialize, Serialize}; |
11 | |
12 | use crate::{ |
13 | error::SpdxExpressionError, |
14 | parser::{parse_expression, simple_expression}, |
15 | }; |
16 | |
17 | /// Simple SPDX license expression. |
18 | #[derive (Debug, Clone, PartialEq, Eq, Hash)] |
19 | pub struct SimpleExpression { |
20 | /// The license identifier. |
21 | pub identifier: String, |
22 | |
23 | /// Optional DocumentRef for the expression. |
24 | pub document_ref: Option<String>, |
25 | |
26 | /// `true` if the expression is a user defined license reference. |
27 | pub license_ref: bool, |
28 | } |
29 | |
30 | impl Serialize for SimpleExpression { |
31 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
32 | where |
33 | S: serde::Serializer, |
34 | { |
35 | serializer.collect_str(self) |
36 | } |
37 | } |
38 | |
39 | struct SimpleExpressionVisitor; |
40 | |
41 | impl<'de> Visitor<'de> for SimpleExpressionVisitor { |
42 | type Value = SimpleExpression; |
43 | |
44 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { |
45 | formatter.write_str("a syntactically valid SPDX simple expression" ) |
46 | } |
47 | |
48 | fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> |
49 | where |
50 | E: serde::de::Error, |
51 | { |
52 | SimpleExpression::parse(v) |
53 | .map_err(|err| E::custom(format!("error parsing the expression: {}" , err))) |
54 | } |
55 | |
56 | fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> |
57 | where |
58 | E: serde::de::Error, |
59 | { |
60 | self.visit_str(v) |
61 | } |
62 | |
63 | fn visit_string<E>(self, v: String) -> Result<Self::Value, E> |
64 | where |
65 | E: serde::de::Error, |
66 | { |
67 | self.visit_str(&v) |
68 | } |
69 | } |
70 | |
71 | impl<'de> Deserialize<'de> for SimpleExpression { |
72 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
73 | where |
74 | D: serde::Deserializer<'de>, |
75 | { |
76 | deserializer.deserialize_str(visitor:SimpleExpressionVisitor) |
77 | } |
78 | } |
79 | |
80 | impl Display for SimpleExpression { |
81 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
82 | let document_ref: String = match &self.document_ref { |
83 | Some(document_ref: &String) => { |
84 | format!("DocumentRef- {}:" , document_ref) |
85 | } |
86 | None => "" .to_string(), |
87 | }; |
88 | |
89 | let license_ref: &str = if self.license_ref { "LicenseRef-" } else { "" }; |
90 | write!( |
91 | f, |
92 | " {document_ref}{license_ref}{identifier}" , |
93 | identifier = self.identifier |
94 | ) |
95 | } |
96 | } |
97 | |
98 | impl SimpleExpression { |
99 | /// Create a new simple expression. |
100 | pub const fn new(identifier: String, document_ref: Option<String>, license_ref: bool) -> Self { |
101 | Self { |
102 | identifier, |
103 | document_ref, |
104 | license_ref, |
105 | } |
106 | } |
107 | |
108 | /// Parse a simple expression. |
109 | /// |
110 | /// # Examples |
111 | /// |
112 | /// ``` |
113 | /// # use spdx_expression::SimpleExpression; |
114 | /// # use spdx_expression::SpdxExpressionError; |
115 | /// # |
116 | /// let expression = SimpleExpression::parse("MIT" )?; |
117 | /// # Ok::<(), SpdxExpressionError>(()) |
118 | /// ``` |
119 | /// |
120 | /// The function will only accept simple expressions, compound expressions will fail. |
121 | /// |
122 | /// ``` |
123 | /// # use spdx_expression::SimpleExpression; |
124 | /// # |
125 | /// let expression = SimpleExpression::parse("MIT OR ISC" ); |
126 | /// assert!(expression.is_err()); |
127 | /// ``` |
128 | /// |
129 | /// # Errors |
130 | /// |
131 | /// Fails if parsing fails. |
132 | pub fn parse(expression: &str) -> Result<Self, SpdxExpressionError> { |
133 | let (remaining, result) = simple_expression(expression)?; |
134 | |
135 | if remaining.is_empty() { |
136 | Ok(result) |
137 | } else { |
138 | Err(SpdxExpressionError::Parse(expression.to_string())) |
139 | } |
140 | } |
141 | } |
142 | |
143 | #[derive (Debug, Clone, PartialEq, Eq)] |
144 | pub struct WithExpression { |
145 | pub license: SimpleExpression, |
146 | pub exception: String, |
147 | } |
148 | |
149 | impl WithExpression { |
150 | pub const fn new(license: SimpleExpression, exception: String) -> Self { |
151 | Self { license, exception } |
152 | } |
153 | } |
154 | |
155 | impl Display for WithExpression { |
156 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
157 | write!( |
158 | f, |
159 | " {license} WITH {exception}" , |
160 | license = self.license, |
161 | exception = self.exception |
162 | ) |
163 | } |
164 | } |
165 | |
166 | #[derive (Debug, PartialEq, Clone, Eq)] |
167 | pub enum ExpressionVariant { |
168 | Simple(SimpleExpression), |
169 | With(WithExpression), |
170 | And(Box<Self>, Box<Self>), |
171 | Or(Box<Self>, Box<Self>), |
172 | Parens(Box<Self>), |
173 | } |
174 | |
175 | impl Display for ExpressionVariant { |
176 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
177 | use self::ExpressionVariant::{And, Or, Parens, Simple, With}; |
178 | |
179 | match self { |
180 | Simple(expression: &SimpleExpression) => write!(f, " {expression}" ), |
181 | With(expression: &WithExpression) => write!(f, " {expression}" ), |
182 | And(left: &Box, right: &Box) => write!(f, " {left} AND {right}" ), |
183 | Or(left: &Box, right: &Box) => write!(f, " {left} OR {right}" ), |
184 | Parens(expression: &Box) => write!(f, "( {expression})" ), |
185 | } |
186 | } |
187 | } |
188 | |
189 | impl ExpressionVariant { |
190 | pub fn parse(i: &str) -> Result<Self, SpdxExpressionError> { |
191 | let (remaining, expression) = parse_expression(i) |
192 | .finish() |
193 | .map_err(|_| SpdxExpressionError::Parse(i.to_string()))?; |
194 | |
195 | if remaining.is_empty() { |
196 | Ok(expression) |
197 | } else { |
198 | Err(SpdxExpressionError::Parse(i.to_string())) |
199 | } |
200 | } |
201 | |
202 | pub fn licenses(&self) -> HashSet<&SimpleExpression> { |
203 | let mut expressions = HashSet::new(); |
204 | |
205 | match self { |
206 | ExpressionVariant::Simple(expression) => { |
207 | expressions.insert(expression); |
208 | } |
209 | ExpressionVariant::With(expression) => { |
210 | expressions.insert(&expression.license); |
211 | } |
212 | ExpressionVariant::And(left, right) | ExpressionVariant::Or(left, right) => { |
213 | expressions.extend(left.licenses()); |
214 | expressions.extend(right.licenses()); |
215 | } |
216 | ExpressionVariant::Parens(expression) => { |
217 | expressions.extend(expression.licenses()); |
218 | } |
219 | } |
220 | |
221 | expressions |
222 | } |
223 | |
224 | pub fn exceptions(&self) -> HashSet<&str> { |
225 | let mut expressions = HashSet::new(); |
226 | |
227 | match self { |
228 | ExpressionVariant::Simple(_) => {} |
229 | ExpressionVariant::With(expression) => { |
230 | expressions.insert(expression.exception.as_str()); |
231 | } |
232 | ExpressionVariant::And(left, right) | ExpressionVariant::Or(left, right) => { |
233 | expressions.extend(left.exceptions()); |
234 | expressions.extend(right.exceptions()); |
235 | } |
236 | ExpressionVariant::Parens(expression) => { |
237 | expressions.extend(expression.exceptions()); |
238 | } |
239 | } |
240 | |
241 | expressions |
242 | } |
243 | } |
244 | |
245 | #[cfg (test)] |
246 | mod tests { |
247 | use std::iter::FromIterator; |
248 | |
249 | use serde_json::Value; |
250 | |
251 | use super::*; |
252 | |
253 | #[test ] |
254 | fn display_simple_correctly() { |
255 | let expression = |
256 | ExpressionVariant::Simple(SimpleExpression::new("MIT" .to_string(), None, false)); |
257 | assert_eq!(expression.to_string(), "MIT" .to_string()); |
258 | } |
259 | |
260 | #[test ] |
261 | fn display_licenseref_correctly() { |
262 | let expression = |
263 | ExpressionVariant::Simple(SimpleExpression::new("license" .to_string(), None, true)); |
264 | assert_eq!(expression.to_string(), "LicenseRef-license" .to_string()); |
265 | } |
266 | |
267 | #[test ] |
268 | fn display_documentref_correctly() { |
269 | let expression = ExpressionVariant::Simple(SimpleExpression::new( |
270 | "license" .to_string(), |
271 | Some("document" .to_string()), |
272 | true, |
273 | )); |
274 | assert_eq!( |
275 | expression.to_string(), |
276 | "DocumentRef-document:LicenseRef-license" .to_string() |
277 | ); |
278 | } |
279 | |
280 | #[test ] |
281 | fn display_with_expression_correctly() { |
282 | let expression = ExpressionVariant::With(WithExpression::new( |
283 | SimpleExpression::new("license" .to_string(), None, false), |
284 | "exception" .to_string(), |
285 | )); |
286 | assert_eq!(expression.to_string(), "license WITH exception" .to_string()); |
287 | } |
288 | |
289 | #[test ] |
290 | fn display_and_expression_correctly() { |
291 | let expression = ExpressionVariant::And( |
292 | Box::new(ExpressionVariant::And( |
293 | Box::new(ExpressionVariant::Simple(SimpleExpression::new( |
294 | "license1" .to_string(), |
295 | None, |
296 | false, |
297 | ))), |
298 | Box::new(ExpressionVariant::Simple(SimpleExpression::new( |
299 | "license2" .to_string(), |
300 | None, |
301 | false, |
302 | ))), |
303 | )), |
304 | Box::new(ExpressionVariant::Simple(SimpleExpression::new( |
305 | "license3" .to_string(), |
306 | None, |
307 | false, |
308 | ))), |
309 | ); |
310 | assert_eq!( |
311 | expression.to_string(), |
312 | "license1 AND license2 AND license3" .to_string() |
313 | ); |
314 | } |
315 | |
316 | #[test ] |
317 | fn display_or_expression_correctly() { |
318 | let expression = ExpressionVariant::Or( |
319 | Box::new(ExpressionVariant::Or( |
320 | Box::new(ExpressionVariant::Simple(SimpleExpression::new( |
321 | "license1" .to_string(), |
322 | None, |
323 | false, |
324 | ))), |
325 | Box::new(ExpressionVariant::Simple(SimpleExpression::new( |
326 | "license2" .to_string(), |
327 | None, |
328 | false, |
329 | ))), |
330 | )), |
331 | Box::new(ExpressionVariant::Simple(SimpleExpression::new( |
332 | "license3" .to_string(), |
333 | None, |
334 | false, |
335 | ))), |
336 | ); |
337 | assert_eq!( |
338 | expression.to_string(), |
339 | "license1 OR license2 OR license3" .to_string() |
340 | ); |
341 | } |
342 | |
343 | #[test ] |
344 | fn get_licenses_correctly() { |
345 | let expression = ExpressionVariant::And( |
346 | Box::new(ExpressionVariant::Simple(SimpleExpression::new( |
347 | "license1+" .to_string(), |
348 | None, |
349 | false, |
350 | ))), |
351 | Box::new(ExpressionVariant::Parens(Box::new(ExpressionVariant::Or( |
352 | Box::new(ExpressionVariant::Parens(Box::new( |
353 | ExpressionVariant::With(WithExpression::new( |
354 | SimpleExpression::new("license2" .to_string(), None, false), |
355 | "exception1" .to_string(), |
356 | )), |
357 | ))), |
358 | Box::new(ExpressionVariant::And( |
359 | Box::new(ExpressionVariant::Simple(SimpleExpression::new( |
360 | "license3+" .to_string(), |
361 | None, |
362 | false, |
363 | ))), |
364 | Box::new(ExpressionVariant::With(WithExpression::new( |
365 | SimpleExpression::new("license4" .to_string(), None, false), |
366 | "exception2" .to_string(), |
367 | ))), |
368 | )), |
369 | )))), |
370 | ); |
371 | |
372 | assert_eq!( |
373 | expression.licenses(), |
374 | HashSet::from_iter([ |
375 | &SimpleExpression::new("license1+" .to_string(), None, false), |
376 | &SimpleExpression::new("license2" .to_string(), None, false), |
377 | &SimpleExpression::new("license3+" .to_string(), None, false), |
378 | &SimpleExpression::new("license4" .to_string(), None, false), |
379 | ]) |
380 | ); |
381 | } |
382 | #[test ] |
383 | fn get_exceptions_correctly() { |
384 | let expression = ExpressionVariant::And( |
385 | Box::new(ExpressionVariant::Simple(SimpleExpression::new( |
386 | "license1+" .to_string(), |
387 | None, |
388 | false, |
389 | ))), |
390 | Box::new(ExpressionVariant::Parens(Box::new(ExpressionVariant::Or( |
391 | Box::new(ExpressionVariant::Parens(Box::new( |
392 | ExpressionVariant::With(WithExpression::new( |
393 | SimpleExpression::new("license2" .to_string(), None, false), |
394 | "exception1" .to_string(), |
395 | )), |
396 | ))), |
397 | Box::new(ExpressionVariant::And( |
398 | Box::new(ExpressionVariant::Simple(SimpleExpression::new( |
399 | "license3+" .to_string(), |
400 | None, |
401 | false, |
402 | ))), |
403 | Box::new(ExpressionVariant::With(WithExpression::new( |
404 | SimpleExpression::new("license4" .to_string(), None, false), |
405 | "exception2" .to_string(), |
406 | ))), |
407 | )), |
408 | )))), |
409 | ); |
410 | |
411 | assert_eq!( |
412 | expression.exceptions(), |
413 | HashSet::from_iter(["exception1" , "exception2" ]) |
414 | ); |
415 | } |
416 | |
417 | #[test ] |
418 | fn parse_simple_expression() { |
419 | let expression = SimpleExpression::parse("MIT" ).unwrap(); |
420 | assert_eq!( |
421 | expression, |
422 | SimpleExpression::new("MIT" .to_string(), None, false) |
423 | ); |
424 | |
425 | let expression = SimpleExpression::parse("MIT OR ISC" ); |
426 | assert!(expression.is_err()); |
427 | |
428 | let expression = SimpleExpression::parse("GPL-2.0-only WITH Classpath-exception-2.0" ); |
429 | assert!(expression.is_err()); |
430 | } |
431 | |
432 | #[test ] |
433 | fn serialize_simple_expression_correctly() { |
434 | let expression = SimpleExpression::parse("MIT" ).unwrap(); |
435 | |
436 | let value = serde_json::to_value(expression).unwrap(); |
437 | |
438 | assert_eq!(value, Value::String("MIT" .to_string())); |
439 | } |
440 | |
441 | #[test ] |
442 | fn deserialize_simple_expression_correctly() { |
443 | let expected = SimpleExpression::parse("LicenseRef-license1" ).unwrap(); |
444 | |
445 | let value = Value::String("LicenseRef-license1" .to_string()); |
446 | |
447 | let actual: SimpleExpression = serde_json::from_value(value).unwrap(); |
448 | |
449 | assert_eq!(actual, expected); |
450 | } |
451 | } |
452 | |