summaryrefslogtreecommitdiffstats
path: root/vendor/spdx-expression
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:25 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:25 +0000
commit5363f350887b1e5b5dd21a86f88c8af9d7fea6da (patch)
tree35ca005eb6e0e9a1ba3bb5dbc033209ad445dc17 /vendor/spdx-expression
parentAdding debian version 1.66.0+dfsg1-1. (diff)
downloadrustc-5363f350887b1e5b5dd21a86f88c8af9d7fea6da.tar.xz
rustc-5363f350887b1e5b5dd21a86f88c8af9d7fea6da.zip
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/spdx-expression')
-rw-r--r--vendor/spdx-expression/.cargo-checksum.json1
-rw-r--r--vendor/spdx-expression/CHANGELOG.md36
-rw-r--r--vendor/spdx-expression/Cargo.toml45
-rw-r--r--vendor/spdx-expression/LICENSES/MIT.txt9
-rw-r--r--vendor/spdx-expression/README.md17
-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
10 files changed, 1327 insertions, 0 deletions
diff --git a/vendor/spdx-expression/.cargo-checksum.json b/vendor/spdx-expression/.cargo-checksum.json
new file mode 100644
index 000000000..3e106bd81
--- /dev/null
+++ b/vendor/spdx-expression/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"19d395f425b5360296ad8cabb7e0a402f402810fa91cbe035abba8071e8940ee","Cargo.toml":"4ff6f16b2616fec9331034e0aeaf74d7708724af1946a85213ae05c3362bd347","LICENSES/MIT.txt":"b85dcd3e453d05982552c52b5fc9e0bdd6d23c6f8e844b984a88af32570b0cc0","README.md":"71a8e862aebbda3691dac42503bbd02ee0606794447647b061b06756f1d6e43b","src/error.rs":"f2112d3180519f7a729fbdc0d055232e95749314affd898e6e981828ca205709","src/expression.rs":"911137e4d9c8cef73c6582b6c04afb41c07b172c47122002b68b6136f5bfd996","src/expression_variant.rs":"475b863f0c7f8958c1c46b55c5285e53888a942f498c598dfb3192d647ac7813","src/lib.rs":"d35e39d49935998f6f263df895724ecc45cbd6f6d8b4dc4252a0abd13721e50f","src/parser.rs":"9b0b2dc2a59e17ba3820e1eff75634d6a5d7dd0e03e7968fcdd5db36dac22d8a"},"package":"53d7ac03c67c572d85049d6db815e20a4a19b41b3d5cca732ac582342021ad77"} \ No newline at end of file
diff --git a/vendor/spdx-expression/CHANGELOG.md b/vendor/spdx-expression/CHANGELOG.md
new file mode 100644
index 000000000..08ff711a1
--- /dev/null
+++ b/vendor/spdx-expression/CHANGELOG.md
@@ -0,0 +1,36 @@
+<!--
+SPDX-FileCopyrightText: 2022 HH Partners
+
+SPDX-License-Identifier: MIT
+ -->
+
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [0.5.2] - 2022-04-13
+
+### Added
+
+- Implement `(de)serialize` for `SimpleExpression`.
+
+## [0.5.1] - 2022-04-13
+
+### Added
+
+- Add function to `SimpleExpression` from a `&str`.
+
+## [0.5.0] - 2022-04-12
+
+### Changed
+
+- Started keeping a changelog.
+
+[unreleased]: https://github.com/doubleopen-project/spdx-expression/compare/v0.5.2...HEAD
+[0.5.2]: https://github.com/doubleopen-project/spdx-expression/compare/v0.5.1...v0.5.2
+[0.5.1]: https://github.com/doubleopen-project/spdx-expression/compare/v0.5.0...v0.5.1
diff --git a/vendor/spdx-expression/Cargo.toml b/vendor/spdx-expression/Cargo.toml
new file mode 100644
index 000000000..d4feac30e
--- /dev/null
+++ b/vendor/spdx-expression/Cargo.toml
@@ -0,0 +1,45 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "spdx-expression"
+version = "0.5.2"
+authors = ["Mikko Murto <mikko.murto@hhpartners.fi>"]
+description = "SPDX Expression"
+homepage = "https://github.com/doubleopen-project/spdx-expression"
+documentation = "https://docs.rs/spdx-expression"
+readme = "README.md"
+keywords = ["SPDX"]
+categories = ["data-structures"]
+license = "MIT"
+repository = "https://github.com/doubleopen-project/spdx-expression"
+
+[dependencies.nom]
+version = "7"
+
+[dependencies.serde]
+version = "1"
+
+[dependencies.thiserror]
+version = "1"
+
+[dependencies.tracing]
+version = "0.1"
+
+[dev-dependencies.pretty_assertions]
+version = "1"
+
+[dev-dependencies.serde_json]
+version = "1"
+
+[dev-dependencies.tracing-subscriber]
+version = "0.3"
diff --git a/vendor/spdx-expression/LICENSES/MIT.txt b/vendor/spdx-expression/LICENSES/MIT.txt
new file mode 100644
index 000000000..2071b23b0
--- /dev/null
+++ b/vendor/spdx-expression/LICENSES/MIT.txt
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) <year> <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/spdx-expression/README.md b/vendor/spdx-expression/README.md
new file mode 100644
index 000000000..9d7d9c27c
--- /dev/null
+++ b/vendor/spdx-expression/README.md
@@ -0,0 +1,17 @@
+<!--
+SPDX-FileCopyrightText: 2022 HH Partners
+
+SPDX-License-Identifier: MIT
+-->
+
+# SPDX License Expressions in Rust
+
+[![crates.io-icon]][crates.io] [![docs.rs-icon]][docs.rs]
+
+Library for parsing and interacting with [SPDX License Expressions][spdx-expression].
+
+[crates.io-icon]: https://img.shields.io/crates/v/spdx-expression
+[crates.io]: https://crates.io/crates/spdx-expression
+[docs.rs-icon]: https://img.shields.io/docsrs/spdx-expression
+[docs.rs]: https://docs.rs/spdx-expression
+[spdx-expression]: https://spdx.github.io/spdx-spec/SPDX-license-expressions/
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());
+ }
+}