use std::fmt::{self, Display, Formatter};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Version {
Unknown,
Semantic(u64, u64, u64),
Rolling(Option<String>),
Custom(String),
}
impl Version {
pub fn from_string<S: Into<String> + AsRef<str>>(s: S) -> Self {
if s.as_ref().is_empty() {
Self::Unknown
} else if let Some((major, minor, patch)) = parse_version(s.as_ref()) {
Self::Semantic(major, minor, patch)
} else {
Self::Custom(s.into())
}
}
}
impl Default for Version {
fn default() -> Self {
Version::Unknown
}
}
impl Display for Version {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
Self::Unknown => f.write_str("Unknown"),
Self::Semantic(major, minor, patch) => write!(f, "{}.{}.{}", major, minor, patch),
Self::Rolling(ref date) => {
let date = match date {
Some(date) => format!(" ({})", date),
None => "".to_owned(),
};
write!(f, "Rolling Release{}", date)
}
Self::Custom(ref version) => write!(f, "{}", version),
}
}
}
fn parse_version(s: &str) -> Option<(u64, u64, u64)> {
let mut iter = s.trim().split_terminator('.').fuse();
let major = iter.next().and_then(|s| s.parse().ok())?;
let minor = iter.next().unwrap_or("0").parse().ok()?;
let patch = iter.next().unwrap_or("0").parse().ok()?;
if iter.next().is_some() {
return None;
}
Some((major, minor, patch))
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn parse_semantic_version() {
let data = [
("", None),
("version", None),
("1", Some((1, 0, 0))),
("1.", Some((1, 0, 0))),
("1.2", Some((1, 2, 0))),
("1.2.", Some((1, 2, 0))),
("1.2.3", Some((1, 2, 3))),
("1.2.3.", Some((1, 2, 3))),
("1.2.3. ", Some((1, 2, 3))),
(" 1.2.3.", Some((1, 2, 3))),
(" 1.2.3. ", Some((1, 2, 3))),
("1.2.3.4", None),
("1.2.3.4.5.6.7.8.9", None),
];
for (s, expected) in &data {
let result = parse_version(s);
assert_eq!(expected, &result);
}
}
#[test]
fn from_string() {
let custom_version = "some version";
let data = [
("", Version::Unknown),
("1.2.3", Version::Semantic(1, 2, 3)),
(custom_version, Version::Custom(custom_version.to_owned())),
];
for (s, expected) in &data {
let version = Version::from_string(*s);
assert_eq!(expected, &version);
}
}
#[test]
fn default() {
assert_eq!(Version::Unknown, Version::default());
}
#[test]
fn display() {
let data = [
(Version::Unknown, "Unknown"),
(Version::Semantic(1, 5, 0), "1.5.0"),
(Version::Rolling(None), "Rolling Release"),
(
Version::Rolling(Some("date".to_owned())),
"Rolling Release (date)",
),
];
for (version, expected) in &data {
assert_eq!(expected, &version.to_string());
}
}
}