diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:32 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:32 +0000 |
commit | 4547b622d8d29df964fa2914213088b148c498fc (patch) | |
tree | 9fc6b25f3c3add6b745be9a2400a6e96140046e9 /vendor/spdx-expression/src/expression.rs | |
parent | Releasing progress-linux version 1.66.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-4547b622d8d29df964fa2914213088b148c498fc.tar.xz rustc-4547b622d8d29df964fa2914213088b148c498fc.zip |
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/spdx-expression/src/expression.rs')
-rw-r--r-- | vendor/spdx-expression/src/expression.rs | 322 |
1 files changed, 322 insertions, 0 deletions
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); + } +} |