use crate::{Error, Versioning};
use nom::IResult;
use nom::bytes::complete::tag;
use nom::combinator::map;
use nom::{Parser, branch::alt};
use std::str::FromStr;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serializer, de::Error as _};
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Op {
Exact,
Greater,
GreaterEq,
Less,
LessEq,
Tilde,
Caret,
Wildcard,
}
impl std::fmt::Display for Op {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Op::Exact => write!(f, "="),
Op::Greater => write!(f, ">"),
Op::GreaterEq => write!(f, ">="),
Op::Less => write!(f, "<"),
Op::LessEq => write!(f, "<="),
Op::Tilde => write!(f, "~"),
Op::Caret => write!(f, "^"),
Op::Wildcard => write!(f, "*"),
}
}
}
impl Op {
fn parse(i: &str) -> IResult<&str, Op> {
alt((
map(tag("="), |_| Op::Exact),
map(tag(">="), |_| Op::GreaterEq),
map(tag(">"), |_| Op::Greater),
map(tag("<="), |_| Op::LessEq),
map(tag("<"), |_| Op::Less),
map(tag("~"), |_| Op::Tilde),
map(tag("^"), |_| Op::Caret),
map(tag("*"), |_| Op::Wildcard),
))
.parse(i)
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct Requirement {
pub op: Op,
pub version: Option<Versioning>,
}
impl Requirement {
pub fn new(s: &str) -> Option<Self> {
match Requirement::parse(s) {
Ok(("", r)) => Some(r),
_ => None,
}
}
fn matches_tilde(&self, other: &Versioning) -> bool {
self.version
.as_ref()
.is_some_and(|v| v.matches_tilde(other))
}
fn matches_caret(&self, other: &Versioning) -> bool {
self.version
.as_ref()
.is_some_and(|v| v.matches_caret(other))
}
pub fn matches(&self, other: &Versioning) -> bool {
if let Some(version) = &self.version {
match self.op {
Op::Exact => other == version,
Op::Greater => other > version,
Op::GreaterEq => other >= version,
Op::Less => other < version,
Op::LessEq => other <= version,
Op::Tilde => self.matches_tilde(other),
Op::Caret => self.matches_caret(other),
Op::Wildcard => true,
}
} else {
matches!(self.op, Op::Wildcard)
}
}
pub fn parse(i: &str) -> IResult<&str, Requirement> {
let (i, op) = Op::parse(i)?;
let (i, req) = match op {
Op::Wildcard => {
let req = Requirement { op, version: None };
(i, req)
}
_ => {
let (i, vr) = Versioning::parse(i)?;
let req = Requirement {
op,
version: Some(vr),
};
(i, req)
}
};
Ok((i, req))
}
#[cfg(feature = "serde")]
pub fn deserialize<'de, D>(deserializer: D) -> Result<Requirement, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
s.parse().map_err(D::Error::custom)
}
#[cfg(feature = "serde")]
pub fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s: String = self.to_string();
serializer.serialize_str(&s)
}
}
impl FromStr for Requirement {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Requirement::new(s).ok_or_else(|| Error::IllegalOp(s.to_string()))
}
}
impl Default for Requirement {
fn default() -> Self {
Requirement {
op: Op::Wildcard,
version: None,
}
}
}
impl std::fmt::Display for Requirement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let version = self
.version
.as_ref()
.map(|v| v.to_string())
.unwrap_or_default();
write!(f, "{}{}", self.op, version,)
}
}