summaryrefslogtreecommitdiffstats
path: root/vendor/spdx-expression/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/spdx-expression/src')
-rw-r--r--vendor/spdx-expression/src/error.rs21
-rw-r--r--vendor/spdx-expression/src/expression.rs322
-rw-r--r--vendor/spdx-expression/src/expression_variant.rs451
-rw-r--r--vendor/spdx-expression/src/lib.rs16
-rw-r--r--vendor/spdx-expression/src/parser.rs409
5 files changed, 1219 insertions, 0 deletions
diff --git a/vendor/spdx-expression/src/error.rs b/vendor/spdx-expression/src/error.rs
new file mode 100644
index 000000000..7e432f3d4
--- /dev/null
+++ b/vendor/spdx-expression/src/error.rs
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: 2022 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+//! Errors of the library.
+
+/// Custom error struct.
+#[derive(thiserror::Error, Debug)]
+pub enum SpdxExpressionError {
+ #[error("Parsing for expression `{0}` failed.")]
+ Parse(String),
+
+ #[error("Error parsing the SPDX Expression {0}.")]
+ Nom(String),
+}
+
+impl From<nom::Err<nom::error::Error<&str>>> for SpdxExpressionError {
+ fn from(err: nom::Err<nom::error::Error<&str>>) -> Self {
+ Self::Nom(err.to_string())
+ }
+}
diff --git a/vendor/spdx-expression/src/expression.rs b/vendor/spdx-expression/src/expression.rs
new file mode 100644
index 000000000..a303dbae7
--- /dev/null
+++ b/vendor/spdx-expression/src/expression.rs
@@ -0,0 +1,322 @@
+// SPDX-FileCopyrightText: 2022 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+//! The main struct of the library.
+
+use std::{collections::HashSet, fmt::Display, string::ToString};
+
+use serde::{de::Visitor, Deserialize, Serialize};
+
+use crate::{
+ error::SpdxExpressionError,
+ expression_variant::{ExpressionVariant, SimpleExpression},
+};
+
+/// Main struct for SPDX License Expressions.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct SpdxExpression {
+ /// The parsed expression.
+ inner: ExpressionVariant,
+}
+
+impl SpdxExpression {
+ /// Parse `Self` from a string. The input expression needs to be a syntactically valid SPDX
+ /// expression, `NONE` or `NOASSERTION`. The parser accepts license identifiers that are not
+ /// valid SPDX.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use spdx_expression::SpdxExpression;
+ /// # use spdx_expression::SpdxExpressionError;
+ /// #
+ /// let expression = SpdxExpression::parse("MIT")?;
+ /// # Ok::<(), SpdxExpressionError>(())
+ /// ```
+ ///
+ /// License expressions need to be syntactically valid, but they can include license
+ /// identifiers not on the SPDX license list or not specified with `LicenseRef`.
+ ///
+ /// ```
+ /// # use spdx_expression::SpdxExpression;
+ /// # use spdx_expression::SpdxExpressionError;
+ /// #
+ /// let expression = SpdxExpression::parse("MIT OR InvalidLicenseId")?;
+ /// # Ok::<(), SpdxExpressionError>(())
+ /// ```
+ ///
+ /// # Errors
+ ///
+ /// Returns `SpdxExpressionError` if the license expression is not syntactically valid.
+ pub fn parse(expression: &str) -> Result<Self, SpdxExpressionError> {
+ Ok(Self {
+ inner: ExpressionVariant::parse(expression)
+ .map_err(|err| SpdxExpressionError::Parse(err.to_string()))?,
+ })
+ }
+
+ /// Get all license and exception identifiers from the `SpdxExpression`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use std::collections::HashSet;
+ /// # use std::iter::FromIterator;
+ /// # use spdx_expression::SpdxExpression;
+ /// # use spdx_expression::SpdxExpressionError;
+ /// #
+ /// let expression = SpdxExpression::parse("MIT OR Apache-2.0")?;
+ /// let licenses = expression.identifiers();
+ /// assert_eq!(licenses, HashSet::from_iter(["Apache-2.0".to_string(), "MIT".to_string()]));
+ /// # Ok::<(), SpdxExpressionError>(())
+ /// ```
+ ///
+ /// ```
+ /// # use std::collections::HashSet;
+ /// # use std::iter::FromIterator;
+ /// # use spdx_expression::SpdxExpression;
+ /// # use spdx_expression::SpdxExpressionError;
+ /// #
+ /// let expression = SpdxExpression::parse("MIT OR GPL-2.0-only WITH Classpath-exception-2.0")?;
+ /// let licenses = expression.identifiers();
+ /// assert_eq!(
+ /// licenses,
+ /// HashSet::from_iter([
+ /// "MIT".to_string(),
+ /// "GPL-2.0-only".to_string(),
+ /// "Classpath-exception-2.0".to_string()
+ /// ])
+ /// );
+ /// # Ok::<(), SpdxExpressionError>(())
+ /// ```
+ pub fn identifiers(&self) -> HashSet<String> {
+ let mut identifiers = self
+ .licenses()
+ .iter()
+ .map(ToString::to_string)
+ .collect::<HashSet<_>>();
+
+ identifiers.extend(self.exceptions().iter().map(ToString::to_string));
+
+ identifiers
+ }
+
+ /// Get all simple license expressions in `Self`. For licenses with exceptions, returns the
+ /// license without the exception
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use std::collections::HashSet;
+ /// # use std::iter::FromIterator;
+ /// # use spdx_expression::SpdxExpression;
+ /// # use spdx_expression::SpdxExpressionError;
+ /// #
+ /// let expression = SpdxExpression::parse(
+ /// "(MIT OR Apache-2.0 AND (GPL-2.0-only WITH Classpath-exception-2.0 OR ISC))",
+ /// )
+ /// .unwrap();
+ ///
+ /// let licenses = expression.licenses();
+ ///
+ /// assert_eq!(
+ /// licenses
+ /// .iter()
+ /// .map(|&license| license.identifier.as_str())
+ /// .collect::<HashSet<_>>(),
+ /// HashSet::from_iter(["Apache-2.0", "GPL-2.0-only", "ISC", "MIT"])
+ /// );
+ /// # Ok::<(), SpdxExpressionError>(())
+ /// ```
+ pub fn licenses(&self) -> HashSet<&SimpleExpression> {
+ self.inner.licenses()
+ }
+
+ /// Get all exception identifiers for `Self`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use std::collections::HashSet;
+ /// # use std::iter::FromIterator;
+ /// # use spdx_expression::SpdxExpression;
+ /// # use spdx_expression::SpdxExpressionError;
+ /// #
+ /// let expression = SpdxExpression::parse(
+ /// "(MIT OR Apache-2.0 AND (GPL-2.0-only WITH Classpath-exception-2.0 OR ISC))",
+ /// )
+ /// .unwrap();
+ ///
+ /// let exceptions = expression.exceptions();
+ ///
+ /// assert_eq!(exceptions, HashSet::from_iter(["Classpath-exception-2.0"]));
+ /// # Ok::<(), SpdxExpressionError>(())
+ /// ```
+ pub fn exceptions(&self) -> HashSet<&str> {
+ self.inner.exceptions()
+ }
+}
+
+impl Default for SpdxExpression {
+ fn default() -> Self {
+ Self::parse("NOASSERTION").expect("will not fail")
+ }
+}
+
+impl Serialize for SpdxExpression {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serializer.collect_str(self)
+ }
+}
+
+struct SpdxExpressionVisitor;
+
+impl<'de> Visitor<'de> for SpdxExpressionVisitor {
+ type Value = SpdxExpression;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+ formatter.write_str("a syntactically valid SPDX expression")
+ }
+
+ fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ SpdxExpression::parse(v)
+ .map_err(|err| E::custom(format!("error parsing the expression: {}", err)))
+ }
+
+ fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ self.visit_str(v)
+ }
+
+ fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ self.visit_str(&v)
+ }
+}
+
+impl<'de> Deserialize<'de> for SpdxExpression {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ deserializer.deserialize_str(SpdxExpressionVisitor)
+ }
+}
+
+impl Display for SpdxExpression {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.inner)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::iter::FromIterator;
+
+ use serde_json::Value;
+
+ use super::*;
+
+ #[test]
+ fn test_parsing_works() {
+ let expression = SpdxExpression::parse("MIT AND (Apache-2.0 OR ISC)").unwrap();
+ assert_eq!(expression.to_string(), "MIT AND (Apache-2.0 OR ISC)");
+ }
+
+ #[test]
+ fn test_identifiers_from_simple_expression() {
+ let expression = SpdxExpression::parse("MIT").unwrap();
+ let licenses = expression.identifiers();
+ assert_eq!(licenses, HashSet::from_iter(["MIT".to_string()]));
+ }
+
+ #[test]
+ fn test_identifiers_from_compound_or_expression() {
+ let expression = SpdxExpression::parse("MIT OR Apache-2.0").unwrap();
+ let licenses = expression.identifiers();
+ assert_eq!(
+ licenses,
+ HashSet::from_iter(["Apache-2.0".to_string(), "MIT".to_string()])
+ );
+ }
+
+ #[test]
+ fn test_identifiers_from_compound_parentheses_expression() {
+ let expression = SpdxExpression::parse(
+ "(MIT OR Apache-2.0 AND (GPL-2.0-only WITH Classpath-exception-2.0 OR ISC))",
+ )
+ .unwrap();
+ let licenses = expression.identifiers();
+ assert_eq!(
+ licenses,
+ HashSet::from_iter([
+ "Apache-2.0".to_string(),
+ "Classpath-exception-2.0".to_string(),
+ "GPL-2.0-only".to_string(),
+ "ISC".to_string(),
+ "MIT".to_string()
+ ])
+ );
+ }
+
+ #[test]
+ fn test_licenses_from_compound_parentheses_expression() {
+ let expression = SpdxExpression::parse(
+ "(MIT OR Apache-2.0 AND (GPL-2.0-only WITH Classpath-exception-2.0 OR ISC))",
+ )
+ .unwrap();
+
+ let licenses = expression.licenses();
+
+ assert_eq!(
+ licenses
+ .iter()
+ .map(|&license| license.identifier.as_str())
+ .collect::<HashSet<_>>(),
+ HashSet::from_iter(["Apache-2.0", "GPL-2.0-only", "ISC", "MIT"])
+ );
+ }
+
+ #[test]
+ fn test_exceptions_from_compound_parentheses_expression() {
+ let expression = SpdxExpression::parse(
+ "(MIT OR Apache-2.0 AND (GPL-2.0-only WITH Classpath-exception-2.0 OR ISC))",
+ )
+ .unwrap();
+
+ let exceptions = expression.exceptions();
+
+ assert_eq!(exceptions, HashSet::from_iter(["Classpath-exception-2.0"]));
+ }
+
+ #[test]
+ fn serialize_expression_correctly() {
+ let expression = SpdxExpression::parse("MIT OR ISC").unwrap();
+
+ let value = serde_json::to_value(expression).unwrap();
+
+ assert_eq!(value, Value::String("MIT OR ISC".to_string()));
+ }
+
+ #[test]
+ fn deserialize_expression_correctly() {
+ let expected = SpdxExpression::parse("MIT OR ISC").unwrap();
+
+ let value = Value::String("MIT OR ISC".to_string());
+
+ let actual: SpdxExpression = serde_json::from_value(value).unwrap();
+
+ assert_eq!(actual, expected);
+ }
+}
diff --git a/vendor/spdx-expression/src/expression_variant.rs b/vendor/spdx-expression/src/expression_variant.rs
new file mode 100644
index 000000000..626d8e2e1
--- /dev/null
+++ b/vendor/spdx-expression/src/expression_variant.rs
@@ -0,0 +1,451 @@
+// SPDX-FileCopyrightText: 2022 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+//! Private inner structs for [`crate::SpdxExpression`].
+
+use std::{collections::HashSet, fmt::Display};
+
+use nom::Finish;
+use serde::{de::Visitor, Deserialize, Serialize};
+
+use crate::{
+ error::SpdxExpressionError,
+ parser::{parse_expression, simple_expression},
+};
+
+/// Simple SPDX license expression.
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct SimpleExpression {
+ /// The license identifier.
+ pub identifier: String,
+
+ /// Optional DocumentRef for the expression.
+ pub document_ref: Option<String>,
+
+ /// `true` if the expression is a user defined license reference.
+ pub license_ref: bool,
+}
+
+impl Serialize for SimpleExpression {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serializer.collect_str(self)
+ }
+}
+
+struct SimpleExpressionVisitor;
+
+impl<'de> Visitor<'de> for SimpleExpressionVisitor {
+ type Value = SimpleExpression;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+ formatter.write_str("a syntactically valid SPDX simple expression")
+ }
+
+ fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ SimpleExpression::parse(v)
+ .map_err(|err| E::custom(format!("error parsing the expression: {}", err)))
+ }
+
+ fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ self.visit_str(v)
+ }
+
+ fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ self.visit_str(&v)
+ }
+}
+
+impl<'de> Deserialize<'de> for SimpleExpression {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ deserializer.deserialize_str(SimpleExpressionVisitor)
+ }
+}
+
+impl Display for SimpleExpression {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let document_ref = match &self.document_ref {
+ Some(document_ref) => {
+ format!("DocumentRef-{}:", document_ref)
+ }
+ None => "".to_string(),
+ };
+
+ let license_ref = if self.license_ref { "LicenseRef-" } else { "" };
+ write!(
+ f,
+ "{document_ref}{license_ref}{identifier}",
+ identifier = self.identifier
+ )
+ }
+}
+
+impl SimpleExpression {
+ /// Create a new simple expression.
+ pub const fn new(identifier: String, document_ref: Option<String>, license_ref: bool) -> Self {
+ Self {
+ identifier,
+ document_ref,
+ license_ref,
+ }
+ }
+
+ /// Parse a simple expression.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use spdx_expression::SimpleExpression;
+ /// # use spdx_expression::SpdxExpressionError;
+ /// #
+ /// let expression = SimpleExpression::parse("MIT")?;
+ /// # Ok::<(), SpdxExpressionError>(())
+ /// ```
+ ///
+ /// The function will only accept simple expressions, compound expressions will fail.
+ ///
+ /// ```
+ /// # use spdx_expression::SimpleExpression;
+ /// #
+ /// let expression = SimpleExpression::parse("MIT OR ISC");
+ /// assert!(expression.is_err());
+ /// ```
+ ///
+ /// # Errors
+ ///
+ /// Fails if parsing fails.
+ pub fn parse(expression: &str) -> Result<Self, SpdxExpressionError> {
+ let (remaining, result) = simple_expression(expression)?;
+
+ if remaining.is_empty() {
+ Ok(result)
+ } else {
+ Err(SpdxExpressionError::Parse(expression.to_string()))
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct WithExpression {
+ pub license: SimpleExpression,
+ pub exception: String,
+}
+
+impl WithExpression {
+ pub const fn new(license: SimpleExpression, exception: String) -> Self {
+ Self { license, exception }
+ }
+}
+
+impl Display for WithExpression {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{license} WITH {exception}",
+ license = self.license,
+ exception = self.exception
+ )
+ }
+}
+
+#[derive(Debug, PartialEq, Clone, Eq)]
+pub enum ExpressionVariant {
+ Simple(SimpleExpression),
+ With(WithExpression),
+ And(Box<Self>, Box<Self>),
+ Or(Box<Self>, Box<Self>),
+ Parens(Box<Self>),
+}
+
+impl Display for ExpressionVariant {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use self::ExpressionVariant::{And, Or, Parens, Simple, With};
+
+ match self {
+ Simple(expression) => write!(f, "{expression}"),
+ With(expression) => write!(f, "{expression}"),
+ And(left, right) => write!(f, "{left} AND {right}"),
+ Or(left, right) => write!(f, "{left} OR {right}"),
+ Parens(expression) => write!(f, "({expression})"),
+ }
+ }
+}
+
+impl ExpressionVariant {
+ pub fn parse(i: &str) -> Result<Self, SpdxExpressionError> {
+ let (remaining, expression) = parse_expression(i)
+ .finish()
+ .map_err(|_| SpdxExpressionError::Parse(i.to_string()))?;
+
+ if remaining.is_empty() {
+ Ok(expression)
+ } else {
+ Err(SpdxExpressionError::Parse(i.to_string()))
+ }
+ }
+
+ pub fn licenses(&self) -> HashSet<&SimpleExpression> {
+ let mut expressions = HashSet::new();
+
+ match self {
+ ExpressionVariant::Simple(expression) => {
+ expressions.insert(expression);
+ }
+ ExpressionVariant::With(expression) => {
+ expressions.insert(&expression.license);
+ }
+ ExpressionVariant::And(left, right) | ExpressionVariant::Or(left, right) => {
+ expressions.extend(left.licenses());
+ expressions.extend(right.licenses());
+ }
+ ExpressionVariant::Parens(expression) => {
+ expressions.extend(expression.licenses());
+ }
+ }
+
+ expressions
+ }
+
+ pub fn exceptions(&self) -> HashSet<&str> {
+ let mut expressions = HashSet::new();
+
+ match self {
+ ExpressionVariant::Simple(_) => {}
+ ExpressionVariant::With(expression) => {
+ expressions.insert(expression.exception.as_str());
+ }
+ ExpressionVariant::And(left, right) | ExpressionVariant::Or(left, right) => {
+ expressions.extend(left.exceptions());
+ expressions.extend(right.exceptions());
+ }
+ ExpressionVariant::Parens(expression) => {
+ expressions.extend(expression.exceptions());
+ }
+ }
+
+ expressions
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::iter::FromIterator;
+
+ use serde_json::Value;
+
+ use super::*;
+
+ #[test]
+ fn display_simple_correctly() {
+ let expression =
+ ExpressionVariant::Simple(SimpleExpression::new("MIT".to_string(), None, false));
+ assert_eq!(expression.to_string(), "MIT".to_string());
+ }
+
+ #[test]
+ fn display_licenseref_correctly() {
+ let expression =
+ ExpressionVariant::Simple(SimpleExpression::new("license".to_string(), None, true));
+ assert_eq!(expression.to_string(), "LicenseRef-license".to_string());
+ }
+
+ #[test]
+ fn display_documentref_correctly() {
+ let expression = ExpressionVariant::Simple(SimpleExpression::new(
+ "license".to_string(),
+ Some("document".to_string()),
+ true,
+ ));
+ assert_eq!(
+ expression.to_string(),
+ "DocumentRef-document:LicenseRef-license".to_string()
+ );
+ }
+
+ #[test]
+ fn display_with_expression_correctly() {
+ let expression = ExpressionVariant::With(WithExpression::new(
+ SimpleExpression::new("license".to_string(), None, false),
+ "exception".to_string(),
+ ));
+ assert_eq!(expression.to_string(), "license WITH exception".to_string());
+ }
+
+ #[test]
+ fn display_and_expression_correctly() {
+ let expression = ExpressionVariant::And(
+ Box::new(ExpressionVariant::And(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license1".to_string(),
+ None,
+ false,
+ ))),
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license2".to_string(),
+ None,
+ false,
+ ))),
+ )),
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license3".to_string(),
+ None,
+ false,
+ ))),
+ );
+ assert_eq!(
+ expression.to_string(),
+ "license1 AND license2 AND license3".to_string()
+ );
+ }
+
+ #[test]
+ fn display_or_expression_correctly() {
+ let expression = ExpressionVariant::Or(
+ Box::new(ExpressionVariant::Or(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license1".to_string(),
+ None,
+ false,
+ ))),
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license2".to_string(),
+ None,
+ false,
+ ))),
+ )),
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license3".to_string(),
+ None,
+ false,
+ ))),
+ );
+ assert_eq!(
+ expression.to_string(),
+ "license1 OR license2 OR license3".to_string()
+ );
+ }
+
+ #[test]
+ fn get_licenses_correctly() {
+ let expression = ExpressionVariant::And(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license1+".to_string(),
+ None,
+ false,
+ ))),
+ Box::new(ExpressionVariant::Parens(Box::new(ExpressionVariant::Or(
+ Box::new(ExpressionVariant::Parens(Box::new(
+ ExpressionVariant::With(WithExpression::new(
+ SimpleExpression::new("license2".to_string(), None, false),
+ "exception1".to_string(),
+ )),
+ ))),
+ Box::new(ExpressionVariant::And(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license3+".to_string(),
+ None,
+ false,
+ ))),
+ Box::new(ExpressionVariant::With(WithExpression::new(
+ SimpleExpression::new("license4".to_string(), None, false),
+ "exception2".to_string(),
+ ))),
+ )),
+ )))),
+ );
+
+ assert_eq!(
+ expression.licenses(),
+ HashSet::from_iter([
+ &SimpleExpression::new("license1+".to_string(), None, false),
+ &SimpleExpression::new("license2".to_string(), None, false),
+ &SimpleExpression::new("license3+".to_string(), None, false),
+ &SimpleExpression::new("license4".to_string(), None, false),
+ ])
+ );
+ }
+ #[test]
+ fn get_exceptions_correctly() {
+ let expression = ExpressionVariant::And(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license1+".to_string(),
+ None,
+ false,
+ ))),
+ Box::new(ExpressionVariant::Parens(Box::new(ExpressionVariant::Or(
+ Box::new(ExpressionVariant::Parens(Box::new(
+ ExpressionVariant::With(WithExpression::new(
+ SimpleExpression::new("license2".to_string(), None, false),
+ "exception1".to_string(),
+ )),
+ ))),
+ Box::new(ExpressionVariant::And(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license3+".to_string(),
+ None,
+ false,
+ ))),
+ Box::new(ExpressionVariant::With(WithExpression::new(
+ SimpleExpression::new("license4".to_string(), None, false),
+ "exception2".to_string(),
+ ))),
+ )),
+ )))),
+ );
+
+ assert_eq!(
+ expression.exceptions(),
+ HashSet::from_iter(["exception1", "exception2"])
+ );
+ }
+
+ #[test]
+ fn parse_simple_expression() {
+ let expression = SimpleExpression::parse("MIT").unwrap();
+ assert_eq!(
+ expression,
+ SimpleExpression::new("MIT".to_string(), None, false)
+ );
+
+ let expression = SimpleExpression::parse("MIT OR ISC");
+ assert!(expression.is_err());
+
+ let expression = SimpleExpression::parse("GPL-2.0-only WITH Classpath-exception-2.0");
+ assert!(expression.is_err());
+ }
+
+ #[test]
+ fn serialize_simple_expression_correctly() {
+ let expression = SimpleExpression::parse("MIT").unwrap();
+
+ let value = serde_json::to_value(expression).unwrap();
+
+ assert_eq!(value, Value::String("MIT".to_string()));
+ }
+
+ #[test]
+ fn deserialize_simple_expression_correctly() {
+ let expected = SimpleExpression::parse("LicenseRef-license1").unwrap();
+
+ let value = Value::String("LicenseRef-license1".to_string());
+
+ let actual: SimpleExpression = serde_json::from_value(value).unwrap();
+
+ assert_eq!(actual, expected);
+ }
+}
diff --git a/vendor/spdx-expression/src/lib.rs b/vendor/spdx-expression/src/lib.rs
new file mode 100644
index 000000000..e790ec7f6
--- /dev/null
+++ b/vendor/spdx-expression/src/lib.rs
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: 2022 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+#![doc = include_str!("../README.md")]
+#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
+#![allow(clippy::module_name_repetitions, clippy::must_use_candidate)]
+
+mod error;
+mod expression;
+mod expression_variant;
+mod parser;
+
+pub use error::SpdxExpressionError;
+pub use expression::SpdxExpression;
+pub use expression_variant::SimpleExpression;
diff --git a/vendor/spdx-expression/src/parser.rs b/vendor/spdx-expression/src/parser.rs
new file mode 100644
index 000000000..cb93c5b2a
--- /dev/null
+++ b/vendor/spdx-expression/src/parser.rs
@@ -0,0 +1,409 @@
+// SPDX-FileCopyrightText: 2022 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+//! Parsing logic for SPDX Expressions.
+//!
+//! The code is heavily inspired by
+//! <https://github.com/Geal/nom/blob/294ffb3d9e0ade2c3b7ddfff52484b6d643dcce1/tests/arithmetic_ast.rs>
+//! which is licensed under the MIT License. The source project includes the following copyright
+//! statement: Copyright (c) 2014-2019 Geoffroy Couprie.
+
+use nom::{
+ branch::alt,
+ bytes::complete::{tag, tag_no_case, take_while1},
+ character::{
+ complete::{multispace0, multispace1},
+ streaming::char,
+ },
+ combinator::{complete, map, opt, recognize},
+ multi::many0,
+ sequence::{delimited, pair, preceded, separated_pair},
+ AsChar, IResult,
+};
+
+use crate::expression_variant::{ExpressionVariant, SimpleExpression, WithExpression};
+
+#[derive(Debug)]
+enum Operator {
+ And,
+ Or,
+}
+
+fn parentheses(i: &str) -> IResult<&str, ExpressionVariant> {
+ delimited(
+ multispace0,
+ delimited(
+ tag("("),
+ map(or_expression, |e| ExpressionVariant::Parens(Box::new(e))),
+ tag(")"),
+ ),
+ multispace0,
+ )(i)
+}
+
+fn terminal_expression(i: &str) -> IResult<&str, ExpressionVariant> {
+ alt((
+ delimited(multispace0, with_expression, multispace0),
+ map(
+ delimited(multispace0, simple_expression, multispace0),
+ ExpressionVariant::Simple,
+ ),
+ parentheses,
+ ))(i)
+}
+
+fn with_expression(i: &str) -> IResult<&str, ExpressionVariant> {
+ map(
+ separated_pair(
+ simple_expression,
+ delimited(multispace1, tag_no_case("WITH"), multispace1),
+ idstring,
+ ),
+ |(lic, exc)| ExpressionVariant::With(WithExpression::new(lic, exc.to_string())),
+ )(i)
+}
+
+fn fold_expressions(
+ initial: ExpressionVariant,
+ remainder: Vec<(Operator, ExpressionVariant)>,
+) -> ExpressionVariant {
+ remainder.into_iter().fold(initial, |acc, pair| {
+ let (oper, expr) = pair;
+ match oper {
+ Operator::And => ExpressionVariant::And(Box::new(acc), Box::new(expr)),
+ Operator::Or => ExpressionVariant::Or(Box::new(acc), Box::new(expr)),
+ }
+ })
+}
+
+fn and_expression(i: &str) -> IResult<&str, ExpressionVariant> {
+ let (i, initial) = terminal_expression(i)?;
+ let (i, remainder) = many0(|i| {
+ let (i, and) = preceded(tag_no_case("AND"), terminal_expression)(i)?;
+ Ok((i, (Operator::And, and)))
+ })(i)?;
+
+ Ok((i, fold_expressions(initial, remainder)))
+}
+
+fn or_expression(i: &str) -> IResult<&str, ExpressionVariant> {
+ let (i, initial) = and_expression(i)?;
+ let (i, remainder) = many0(|i| {
+ let (i, or) = preceded(tag_no_case("OR"), and_expression)(i)?;
+ Ok((i, (Operator::Or, or)))
+ })(i)?;
+
+ Ok((i, fold_expressions(initial, remainder)))
+}
+
+pub fn parse_expression(i: &str) -> IResult<&str, ExpressionVariant> {
+ or_expression(i)
+}
+
+fn idstring(i: &str) -> IResult<&str, &str> {
+ take_while1(|c: char| c.is_alphanum() || c == '-' || c == '.')(i)
+}
+
+fn license_idstring(i: &str) -> IResult<&str, &str> {
+ recognize(pair(idstring, opt(complete(char('+')))))(i)
+}
+
+fn document_ref(i: &str) -> IResult<&str, &str> {
+ delimited(tag("DocumentRef-"), idstring, char(':'))(i)
+}
+
+fn license_ref(i: &str) -> IResult<&str, (Option<&str>, &str)> {
+ separated_pair(opt(document_ref), tag("LicenseRef-"), idstring)(i)
+}
+
+pub fn simple_expression(i: &str) -> IResult<&str, SimpleExpression> {
+ alt((
+ map(license_ref, |(document_ref, id)| {
+ let document_ref = document_ref.map(std::string::ToString::to_string);
+ SimpleExpression::new(id.to_string(), document_ref, true)
+ }),
+ map(license_idstring, |id| {
+ SimpleExpression::new(id.to_string(), None, false)
+ }),
+ ))(i)
+}
+
+#[cfg(test)]
+mod tests {
+ //! A lot of the test cases for parsing are copied from
+ //! <https://github.com/oss-review-toolkit/ort/blob/6eb18b6d36f59c6d7ec221bad1cf5d4cd6acfc8b/utils/spdx/src/test/kotlin/SpdxExpressionParserTest.kt>
+ //! which is licensed under the Apache License, Version 2.0 and includes the following copyright
+ //! statement:
+ //! Copyright (C) 2017-2019 HERE Europe B.V.
+
+ use super::*;
+
+ use pretty_assertions::assert_eq;
+
+ #[test]
+ fn parse_a_license_id_correctly() {
+ let parsed = ExpressionVariant::parse("spdx.license-id").unwrap();
+ assert_eq!(
+ parsed,
+ ExpressionVariant::Simple(SimpleExpression::new(
+ "spdx.license-id".to_string(),
+ None,
+ false
+ ))
+ );
+ }
+
+ #[test]
+ fn parse_a_license_id_starting_with_a_digit_correctly() {
+ let parsed = ExpressionVariant::parse("0license").unwrap();
+ assert_eq!(
+ parsed,
+ ExpressionVariant::Simple(SimpleExpression::new("0license".to_string(), None, false))
+ );
+ }
+
+ #[test]
+ fn parse_a_license_id_with_any_later_version_correctly() {
+ let parsed = ExpressionVariant::parse("license+").unwrap();
+ assert_eq!(
+ parsed,
+ ExpressionVariant::Simple(SimpleExpression::new("license+".to_string(), None, false))
+ );
+ }
+
+ #[test]
+ fn parse_a_document_ref_correctly() {
+ let parsed = ExpressionVariant::parse("DocumentRef-document:LicenseRef-license").unwrap();
+ assert_eq!(
+ parsed,
+ ExpressionVariant::Simple(SimpleExpression::new(
+ "license".to_string(),
+ Some("document".to_string()),
+ true
+ ))
+ );
+ }
+
+ #[test]
+ fn parse_a_license_ref_correctly() {
+ let parsed = ExpressionVariant::parse("LicenseRef-license").unwrap();
+ assert_eq!(
+ parsed,
+ ExpressionVariant::Simple(SimpleExpression::new("license".to_string(), None, true))
+ );
+ }
+
+ #[test]
+ fn parse_a_with_expression_correctly() {
+ let parsed = ExpressionVariant::parse("license WITH exception").unwrap();
+ assert_eq!(
+ parsed,
+ ExpressionVariant::With(WithExpression::new(
+ SimpleExpression::new("license".to_string(), None, false),
+ "exception".to_string()
+ ))
+ );
+ }
+
+ #[test]
+ fn parse_a_complex_expression_correctly() {
+ let parsed = ExpressionVariant::parse(
+ "license1+ and ((license2 with exception1) OR license3+ AND license4 WITH exception2)",
+ )
+ .unwrap();
+
+ assert_eq!(
+ parsed,
+ ExpressionVariant::And(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license1+".to_string(),
+ None,
+ false
+ ))),
+ Box::new(ExpressionVariant::Parens(Box::new(ExpressionVariant::Or(
+ Box::new(ExpressionVariant::Parens(Box::new(
+ ExpressionVariant::With(WithExpression::new(
+ SimpleExpression::new("license2".to_string(), None, false),
+ "exception1".to_string()
+ ))
+ ))),
+ Box::new(ExpressionVariant::And(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license3+".to_string(),
+ None,
+ false
+ ))),
+ Box::new(ExpressionVariant::With(WithExpression::new(
+ SimpleExpression::new("license4".to_string(), None, false),
+ "exception2".to_string()
+ )))
+ )),
+ ))))
+ )
+ );
+ }
+
+ #[test]
+ fn bind_plus_stronger_than_with() {
+ let parsed = ExpressionVariant::parse("license+ WITH exception").unwrap();
+ assert_eq!(
+ parsed,
+ ExpressionVariant::With(WithExpression::new(
+ SimpleExpression::new("license+".to_string(), None, false),
+ "exception".to_string()
+ ))
+ );
+ }
+
+ #[test]
+ fn bind_with_stronger_than_and() {
+ let parsed = ExpressionVariant::parse("license1 AND license2 WITH exception").unwrap();
+ assert_eq!(
+ parsed,
+ ExpressionVariant::And(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license1".to_string(),
+ None,
+ false
+ ))),
+ Box::new(ExpressionVariant::With(WithExpression::new(
+ SimpleExpression::new("license2".to_string(), None, false),
+ "exception".to_string()
+ )))
+ )
+ );
+ }
+
+ #[test]
+ fn bind_and_stronger_than_or() {
+ let parsed = ExpressionVariant::parse("license1 OR license2 AND license3").unwrap();
+ assert_eq!(
+ parsed,
+ ExpressionVariant::Or(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license1".to_string(),
+ None,
+ false
+ ))),
+ Box::new(ExpressionVariant::And(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license2".to_string(),
+ None,
+ false
+ ))),
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license3".to_string(),
+ None,
+ false
+ )))
+ ))
+ )
+ );
+ }
+
+ #[test]
+ fn bind_the_and_operator_left_associative() {
+ let parsed = ExpressionVariant::parse("license1 AND license2 AND license3").unwrap();
+ assert_eq!(
+ parsed,
+ ExpressionVariant::And(
+ Box::new(ExpressionVariant::And(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license1".to_string(),
+ None,
+ false
+ ))),
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license2".to_string(),
+ None,
+ false
+ )))
+ )),
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license3".to_string(),
+ None,
+ false
+ ))),
+ )
+ );
+ }
+
+ #[test]
+ fn bind_the_or_operator_left_associative() {
+ let parsed = ExpressionVariant::parse("license1 OR license2 OR license3").unwrap();
+ assert_eq!(
+ parsed,
+ ExpressionVariant::Or(
+ Box::new(ExpressionVariant::Or(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license1".to_string(),
+ None,
+ false
+ ))),
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license2".to_string(),
+ None,
+ false
+ )))
+ )),
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license3".to_string(),
+ None,
+ false
+ ))),
+ )
+ );
+ }
+
+ #[test]
+ fn respect_parentheses_for_binding_strength_of_operators() {
+ let parsed = ExpressionVariant::parse("(license1 OR license2) AND license3").unwrap();
+ assert_eq!(
+ parsed,
+ ExpressionVariant::And(
+ Box::new(ExpressionVariant::Parens(Box::new(ExpressionVariant::Or(
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license1".to_string(),
+ None,
+ false
+ ))),
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license2".to_string(),
+ None,
+ false
+ )))
+ )))),
+ Box::new(ExpressionVariant::Simple(SimpleExpression::new(
+ "license3".to_string(),
+ None,
+ false
+ ))),
+ )
+ );
+ }
+
+ #[test]
+ fn fail_if_plus_is_used_in_an_exception_expression() {
+ let parsed = ExpressionVariant::parse("license WITH exception+");
+ assert!(parsed.is_err());
+ }
+
+ #[test]
+ fn fail_if_a_compound_expressions_is_used_before_with() {
+ let parsed = ExpressionVariant::parse("(license1 AND license2) WITH exception");
+ assert!(parsed.is_err());
+ }
+
+ #[test]
+ fn fail_on_an_invalid_symbol() {
+ let parsed = ExpressionVariant::parse("/");
+ assert!(parsed.is_err());
+ }
+
+ #[test]
+ fn fail_on_a_syntax_error() {
+ let parsed = ExpressionVariant::parse("((");
+ assert!(parsed.is_err());
+ }
+}