use crate::prelude::ParsingError;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Version {
pub major: u8,
pub minor: u8,
}
impl Default for Version {
fn default() -> Self {
Version::new(3, 0)
}
}
impl std::fmt::Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}.{}", self.major, self.minor)
}
}
impl std::ops::Add<u8> for Version {
type Output = Version;
fn add(self, major: u8) -> Version {
Version {
major: self.major + major,
minor: self.minor,
}
}
}
impl std::ops::AddAssign<u8> for Version {
fn add_assign(&mut self, major: u8) {
self.major += major;
}
}
impl std::ops::Sub<u8> for Version {
type Output = Version;
fn sub(self, major: u8) -> Version {
if major >= self.major {
Version {
major: 1,
minor: self.minor,
}
} else {
Version {
major: self.major - major,
minor: self.minor,
}
}
}
}
impl std::ops::SubAssign<u8> for Version {
fn sub_assign(&mut self, major: u8) {
if major >= self.major {
self.major = 1;
} else {
self.major -= major;
}
}
}
impl From<Version> for (u8, u8) {
fn from(v: Version) -> Self {
(v.major, v.minor)
}
}
impl std::str::FromStr for Version {
type Err = ParsingError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut digits = s.split('.');
match s.contains('.') {
true => {
let major = digits.next().ok_or(ParsingError::Version)?;
let minor = digits.next().ok_or(ParsingError::Version)?;
let major = major.parse::<u8>().or(Err(ParsingError::Version))?;
let minor = minor.parse::<u8>().or(Err(ParsingError::Version))?;
Ok(Self { major, minor })
},
false => {
let major = digits.next().ok_or(ParsingError::Version)?;
let major = major.parse::<u8>().or(Err(ParsingError::Version))?;
Ok(Self { major, minor: 0 })
},
}
}
}
impl Version {
pub fn new(major: u8, minor: u8) -> Self {
Self { major, minor }
}
}
#[cfg(test)]
mod test {
use super::*;
use std::str::FromStr;
#[test]
fn version() {
let version = Version::from_str("1");
assert!(version.is_ok());
let version = version.unwrap();
assert_eq!(version.major, 1);
assert_eq!(version.minor, 0);
let version = Version::from_str("1.2");
assert!(version.is_ok());
let version = version.unwrap();
assert_eq!(version.major, 1);
assert_eq!(version.minor, 2);
let version = Version::from_str("3.02");
assert!(version.is_ok());
let version = version.unwrap();
assert_eq!(version.major, 3);
assert_eq!(version.minor, 2);
let version = Version::from_str("a.b");
assert!(version.is_err());
}
#[test]
fn version_comparison() {
let v_a = Version::from_str("1.2").unwrap();
let v_b = Version::from_str("3.02").unwrap();
assert!(v_b > v_a);
assert!(v_b != v_a);
}
#[test]
fn version_arithmetics() {
let version = Version::new(3, 2);
assert_eq!(version + 1, Version::new(4, 2));
assert_eq!(version + 2, Version::new(5, 2));
assert_eq!(version - 2, Version::new(1, 2));
assert_eq!(version - 3, Version::new(1, 2));
let (maj, min): (u8, u8) = version.into();
assert_eq!(maj, 3);
assert_eq!(min, 2);
}
}