1// SPDX-FileCopyrightText: 2022 HH Partners
2//
3// SPDX-License-Identifier: MIT
4
5//! Private inner structs for [`crate::SpdxExpression`].
6
7use std::{collections::HashSet, fmt::Display};
8
9use nom::Finish;
10use serde::{de::Visitor, Deserialize, Serialize};
11
12use crate::{
13 error::SpdxExpressionError,
14 parser::{parse_expression, simple_expression},
15};
16
17/// Simple SPDX license expression.
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub 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
30impl 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
39struct SimpleExpressionVisitor;
40
41impl<'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
71impl<'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
80impl 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
98impl 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)]
144pub struct WithExpression {
145 pub license: SimpleExpression,
146 pub exception: String,
147}
148
149impl WithExpression {
150 pub const fn new(license: SimpleExpression, exception: String) -> Self {
151 Self { license, exception }
152 }
153}
154
155impl 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)]
167pub 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
175impl 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
189impl 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)]
246mod 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