use std::hash::Hash; use serde::{Deserialize, Serialize}; use spdx::Expression; /// License field value. /// /// This may either be a single SPDX license expression, or a list of licenses /// or expressions. /// /// A list should be interpreted as being a single expression with members /// joined with `OR`; this library does no such interpretation immediately, so /// as to keep the format of the original document. However, the /// [`License::to_expression`] method does this for convenience. /// /// Note that `Hash`, `PartialEq`, and `Eq` are implemented in term of the /// original strings for the expression. That is, the list of `Apache-2.0` and /// `MIT` may not be equal or hash to the same as `Apache-2.0 OR MIT`. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged, try_from = "ExprInternal", into = "ExprInternal")] pub enum License { /// A single SPDX license expression. Single(Box<Expression>), /// A set of SPDX license expressions (interpreted as joined by `OR`). AnyOf(Vec<Expression>), } impl License { /// Get a single SPDX expression for this License value. pub fn to_expression(&self) -> Expression { match self { Self::Single(exp) => *exp.clone(), Self::AnyOf(exps) => Expression::parse( &exps .iter() .map(|exp| format!("({exp})")) .collect::<Vec<_>>() .join(" OR "), ) .expect("if the original expressions parsed, this one will too"), } } } impl Hash for License { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.to_expression().to_string().hash(state) } } impl PartialEq for License { fn eq(&self, other: &Self) -> bool { self.to_expression().eq(&other.to_expression()) } } impl Eq for License {} #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] enum ExprInternal { Single(String), AnyOf(Vec<String>), } impl TryFrom<ExprInternal> for License { type Error = spdx::ParseError; fn try_from(value: ExprInternal) -> Result<Self, Self::Error> { match value { ExprInternal::Single(expr) => { let expr = Expression::parse(&expr)?; Ok(Self::Single(Box::new(expr))) } ExprInternal::AnyOf(exprs) => { let mut exps = Vec::with_capacity(exprs.len()); for exp in exprs { exps.push(Expression::parse(&exp)?); } Ok(Self::AnyOf(exps)) } } } } impl From<License> for ExprInternal { fn from(license: License) -> Self { match license { License::Single(exp) => Self::Single(exp.to_string()), License::AnyOf(exps) => Self::AnyOf(exps.into_iter().map(|e| e.to_string()).collect()), } } }