diff options
Diffstat (limited to 'vendor/os_info/src/version.rs')
-rw-r--r-- | vendor/os_info/src/version.rs | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/vendor/os_info/src/version.rs b/vendor/os_info/src/version.rs new file mode 100644 index 000000000..20a2c4a61 --- /dev/null +++ b/vendor/os_info/src/version.rs @@ -0,0 +1,150 @@ +use std::fmt::{self, Display, Formatter}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Operating system version. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Version { + /// Unknown version. + Unknown, + /// Semantic version (major.minor.patch). + Semantic(u64, u64, u64), + /// Rolling version. Optionally contains the release date in the string format. + Rolling(Option<String>), + /// Custom version format. + Custom(String), +} + +impl Version { + /// Constructs `VersionType` from the given string. + /// + /// Returns `VersionType::Unknown` if the string is empty. If it can be parsed as a semantic + /// version, then `VersionType::Semantic`, otherwise `VersionType::Custom`. + /// + /// # Examples + /// + /// ``` + /// use os_info::Version; + /// + /// let v = Version::from_string("custom"); + /// assert_eq!(Version::Custom("custom".to_owned()), v); + /// + /// let v = Version::from_string("1.2.3"); + /// assert_eq!(Version::Semantic(1, 2, 3), v); + /// ``` + 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()); + } + } +} |