use std::cmp::Ordering;
use std::fmt::{Debug, Display};
pub struct Version {
pub epoch: Option<u64>, pub components: Vec<String>, pub pre_release: Option<String>, pub build_metadata: Option<String>, }
impl Version {
pub fn parse(version_str: &str) -> Self {
let mut parts = version_str.splitn(2, ':');
let epoch = parts.next().and_then(|s| s.parse::<u64>().ok());
let rest = parts.next().unwrap_or(version_str);
let mut parts = rest.splitn(2, '+');
let version_part = parts.next().unwrap_or(rest);
let build_metadata = parts.next().map(|s| s.to_string());
let mut parts = version_part.splitn(2, '-');
let main_version = parts.next().unwrap_or(version_part);
let pre_release = parts.next().map(|s| s.to_string());
let components: Vec<String> = main_version
.split(['.']) .map(|s| s.to_string())
.collect();
Version {
epoch,
components,
pre_release,
build_metadata,
}
}
}
impl PartialEq for Version {
fn eq(&self, other: &Self) -> bool {
self.epoch == other.epoch
&& self.components == other.components
&& self.pre_release == other.pre_release
&& self.build_metadata == other.build_metadata
}
fn ne(&self, other: &Self) -> bool {
self.epoch != other.epoch
|| self.components != other.components
|| self.pre_release != other.pre_release
|| self.build_metadata != other.build_metadata
}
}
impl Eq for Version {}
impl PartialOrd for Version {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Version {
fn cmp(&self, other: &Self) -> Ordering {
if let Some(epoch_cmp) = self.epoch.partial_cmp(&other.epoch) {
if epoch_cmp != Ordering::Equal {
return epoch_cmp;
}
}
for (a, b) in self.components.iter().zip(&other.components) {
let a_num = a.parse::<u64>().ok();
let b_num = b.parse::<u64>().ok();
match (a_num, b_num) {
(Some(a_num), Some(b_num)) => {
let cmp = a_num.cmp(&b_num);
if cmp != Ordering::Equal {
return cmp;
}
}
(Some(_), None) => return Ordering::Less,
(None, Some(_)) => return Ordering::Greater,
(None, None) => {
let cmp = a.cmp(b);
if cmp != Ordering::Equal {
return cmp;
}
}
}
}
if self.pre_release.is_some() || other.pre_release.is_some() {
return match (&self.pre_release, &other.pre_release) {
(None, None) => Ordering::Equal,
(None, Some(_)) => Ordering::Greater, (Some(_), None) => Ordering::Less, (Some(a), Some(b)) => a.to_lowercase().cmp(&b.to_lowercase()), }
}
self.build_metadata.partial_cmp(&other.build_metadata).unwrap()
}
}
impl Debug for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let epoch = self.epoch.unwrap_or_default();
let components = &self.components;
let pre_release = self.pre_release.clone().unwrap_or_default();
let build_metadata = self.epoch.unwrap_or_default();
write!(f, "epoch:{epoch} components:{components:?} pre_release:{pre_release} build_metadata:{build_metadata}")
}
}
impl Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut string = String::new();
let epoch = self.epoch;
if let Some(epoch) = epoch {
string += epoch.to_string().as_str();
string += ":";
}
let components = &self.components;
for component in components {
string += component.as_str();
string += ".";
}
string.remove(string.len() - 1);
let pre_release = self.pre_release.clone();
if let Some(pre_release) = pre_release {
string += "-";
string += pre_release.as_str();
}
let build_metadata = self.build_metadata.clone();
if let Some(build_metadata) = build_metadata {
string += "+";
string += build_metadata.as_str();
}
write!(f, "{}",string)
}
}
impl Default for Version {
fn default() -> Self {
Version::parse("0.0.1")
}
}