use std::hash::Hash;
use serde::{Deserialize, Serialize};
use spdx::Expression;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged, try_from = "ExprInternal", into = "ExprInternal")]
pub enum License {
Single(Box<Expression>),
AnyOf(Vec<Expression>),
}
impl License {
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()),
}
}
}