use std::cmp::Ordering;
use crate::{Error, Mess, SemVer, Version};
use nom::IResult;
use nom::combinator::map;
use nom::{Parser, branch::alt};
use std::str::FromStr;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, de::Error as _};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum Versioning {
Ideal(SemVer),
General(Version),
Complex(Mess),
}
impl Versioning {
pub fn new<S>(s: S) -> Option<Versioning>
where
S: AsRef<str>,
{
let str = s.as_ref();
SemVer::new(str)
.map(Versioning::Ideal)
.or_else(|| Version::new(str).map(Versioning::General))
.or_else(|| Mess::new(str).map(Versioning::Complex))
}
pub fn parse(i: &str) -> IResult<&str, Versioning> {
alt((
map(SemVer::parse, Versioning::Ideal),
map(Version::parse, Versioning::General),
map(Mess::parse, Versioning::Complex),
))
.parse(i)
}
pub fn is_ideal(&self) -> bool {
matches!(self, Versioning::Ideal(_))
}
pub fn is_general(&self) -> bool {
matches!(self, Versioning::General(_))
}
pub fn is_complex(&self) -> bool {
matches!(self, Versioning::Complex(_))
}
pub fn nth(&self, n: usize) -> Option<u32> {
match self {
Versioning::Ideal(s) if n == 0 => Some(s.major),
Versioning::Ideal(s) if n == 1 => Some(s.minor),
Versioning::Ideal(s) if n == 2 => Some(s.patch),
Versioning::Ideal(_) => None,
Versioning::General(v) => v.nth(n),
Versioning::Complex(m) => m.nth(n),
}
}
pub(crate) fn matches_tilde(&self, other: &Versioning) -> bool {
match (self, other) {
(Versioning::Ideal(a), Versioning::Ideal(b)) => a.matches_tilde(b),
(Versioning::General(a), Versioning::General(b)) => a.matches_tilde(b),
(Versioning::Complex(_), Versioning::Complex(_)) => false,
(_, _) => false,
}
}
pub(crate) fn matches_caret(&self, other: &Versioning) -> bool {
match (self, other) {
(Versioning::Ideal(v1), Versioning::Ideal(v2)) => v1.matches_caret(v2),
(Versioning::General(v1), Versioning::General(v2)) => v1.matches_caret(v2),
(Versioning::Complex(_), Versioning::Complex(_)) => false,
(_, _) => false,
}
}
#[cfg(feature = "serde")]
pub fn deserialize_pretty<'de, D>(deserializer: D) -> Result<Versioning, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
Versioning::new(&s)
.ok_or_else(|| Error::IllegalVersioning(s))
.map_err(D::Error::custom)
}
}
impl PartialOrd for Versioning {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Versioning {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(Versioning::Ideal(a), Versioning::Ideal(b)) => a.cmp(b),
(Versioning::General(a), Versioning::General(b)) => a.cmp(b),
(Versioning::Complex(a), Versioning::Complex(b)) => a.cmp(b),
(Versioning::Ideal(a), Versioning::General(b)) => a.cmp_version(b),
(Versioning::General(a), Versioning::Ideal(b)) => b.cmp_version(a).reverse(),
(Versioning::Ideal(a), Versioning::Complex(b)) => a.cmp_mess(b),
(Versioning::Complex(a), Versioning::Ideal(b)) => b.cmp_mess(a).reverse(),
(Versioning::General(a), Versioning::Complex(b)) => a.cmp_mess(b),
(Versioning::Complex(a), Versioning::General(b)) => b.cmp_mess(a).reverse(),
}
}
}
impl std::fmt::Display for Versioning {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Versioning::Ideal(s) => write!(f, "{}", s),
Versioning::General(v) => write!(f, "{}", v),
Versioning::Complex(m) => write!(f, "{}", m),
}
}
}
impl FromStr for Versioning {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Versioning::new(s).ok_or_else(|| Error::IllegalVersioning(s.to_string()))
}
}
impl TryFrom<&str> for Versioning {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Versioning::from_str(value)
}
}
impl Default for Versioning {
fn default() -> Self {
Self::Ideal(SemVer::default())
}
}