spdx_expression/
expression_variant.rs

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(SimpleExpressionVisitor)
77    }
78}
79
80impl Display for SimpleExpression {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        let document_ref = match &self.document_ref {
83            Some(document_ref) => {
84                format!("DocumentRef-{}:", document_ref)
85            }
86            None => "".to_string(),
87        };
88
89        let license_ref = 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) => write!(f, "{expression}"),
181            With(expression) => write!(f, "{expression}"),
182            And(left, right) => write!(f, "{left} AND {right}"),
183            Or(left, right) => write!(f, "{left} OR {right}"),
184            Parens(expression) => 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}