use core::{
convert::{TryFrom, TryInto},
fmt,
};
use crate::{License, SpdxLicense, ParseError};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Expr {
Single(License),
Or(Or),
And(And),
}
impl From<SpdxLicense> for Expr {
#[inline]
fn from(l: SpdxLicense) -> Self {
Self::Single(l.into())
}
}
impl From<License> for Expr {
#[inline]
fn from(l: License) -> Self {
Self::Single(l)
}
}
impl From<Or> for Expr {
#[inline]
fn from(or: Or) -> Self {
Self::Or(or)
}
}
impl From<And> for Expr {
#[inline]
fn from(and: And) -> Self {
Self::And(and)
}
}
impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expr::Single(ref l) => l.fmt(f),
Expr::Or(ref or) => or.fmt(f),
Expr::And(ref and) => and.fmt(f),
}
}
}
impl<'a> TryFrom<&'a str> for Expr {
type Error = ParseError<'a>;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
use core::{iter, str::Split};
fn handle_iter<'a>(
a: &'a str,
b: &'a str,
iter: Split<'a, &str>,
) -> Vec<License> {
iter::once(a)
.chain(iter::once(b))
.chain(iter)
.map(str::trim)
.map(License::try_from)
.flatten()
.collect()
}
let s = s.trim();
let mut iter_or = s.split(" OR ");
match (iter_or.next(), iter_or.next()) {
(None, _) => {
Err(ParseError::Empty)
},
(Some(s), None) => {
let mut iter_and = s.split(" AND ");
match (iter_and.next(), iter_and.next()) {
(None, _) => {
Err(ParseError::Empty)
},
(Some(s), None) => {
s.try_into().map(Self::Single)
},
(Some(l1), Some(l2)) => {
Ok(And(handle_iter(l1, l2, iter_and)).into())
},
}
},
(Some(l1), Some(l2)) => {
Ok(Or(handle_iter(l1, l2, iter_or)).into())
},
}
}
}
impl PartialEq<License> for Expr {
#[inline]
fn eq(&self, l: &License) -> bool {
if let Expr::Single(e) = self {
e == l
} else {
false
}
}
}
impl PartialEq<str> for Expr {
fn eq(&self, s: &str) -> bool {
let (list, sep) = match self {
Expr::Single(l) => return l.id() == s,
Expr::Or(or) => {
(or.as_slice(), " OR ")
},
Expr::And(and) => {
(and.as_slice(), " AND ")
},
};
let mut iter = s.trim().split(sep).map(str::trim);
for license in list {
match iter.next() {
None => return false,
Some(next) if next != license.id() => return false,
_ => {},
}
}
iter.next().is_none()
}
}
impl PartialEq<Expr> for str {
#[inline]
fn eq(&self, e: &Expr) -> bool {
e == self
}
}
impl Expr {
#[inline]
pub fn parse<'i, I>(input: I) -> Result<Self, ParseError<'i>>
where I: TryInto<Self, Error = ParseError<'i>> + 'i
{
input.try_into()
}
#[inline]
pub fn as_slice(&self) -> &[License] {
match self {
Expr::Single(l) => core::slice::from_ref(l),
Expr::Or(or) => or.as_slice(),
Expr::And(and) => and.as_slice(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Or(Vec<License>);
impl Or {
#[inline]
pub fn as_slice(&self) -> &[License] {
self.0.as_slice()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct And(Vec<License>);
impl And {
#[inline]
pub fn as_slice(&self) -> &[License] {
self.0.as_slice()
}
}
mod impl_display {
use super::*;
type Iter<'a> = core::slice::Iter<'a, License>;
fn display_iter(mut iter: Iter, sep: &str, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(first) = iter.next() {
fmt::Display::fmt(&first, f)?;
} else {
return Ok(());
}
for next in iter {
write!(f, "{}{}", sep, next)?;
}
Ok(())
}
impl fmt::Display for Or {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
display_iter(self.0.iter(), " OR ", f)
}
}
impl fmt::Display for And {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
display_iter(self.0.iter(), " AND ", f)
}
}
}
#[cfg(feature = "serde")]
mod serde {
use core::fmt;
use serde::{
ser::{Serialize, Serializer},
de::{self, Deserialize, Deserializer, Visitor},
};
use super::Expr;
struct ExprVisitor;
impl<'de> Visitor<'de> for ExprVisitor {
type Value = Expr;
#[inline]
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a Expr string")
}
#[inline]
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where E: de::Error,
{
Expr::parse(v).map_err(E::custom)
}
}
impl<'de> Deserialize<'de> for Expr {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>
{
deserializer.deserialize_str(ExprVisitor)
}
}
impl Serialize for Expr {
#[inline]
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.serialize_str(&self.to_string())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn expr_eq() {
let licenses = [SpdxLicense::Mit, SpdxLicense::Apache2];
let licenses = licenses.iter().map(|&l| License::from(l));
for l1 in licenses.clone() {
let expr = l1.id();
let e = Expr::parse(expr).unwrap();
assert_eq!(&e, expr);
assert_eq!(e, Expr::Single(l1.clone()));
for l2 in licenses.clone() {
let exprs = [
format!("{} OR {}", l1, l2),
format!(" {} OR {} ", l1, l2),
format!(" {} OR {} OR {}", l1, l2, l1),
format!("{} AND {}", l1, l2),
format!(" {} AND {} ", l1, l2),
format!(" {} AND {} AND {}", l1, l2, l1),
];
for expr in exprs.iter() {
let e = Expr::parse(expr.as_str()).unwrap();
assert_eq!(&e, expr.as_str());
}
}
}
}
}