1pub use error::*;
2use std::fmt;
3
4#[derive(Debug)]
5pub struct Version {
6 pub major: u8,
7 pub minor: u8,
8 pub build: u8,
9}
10
11impl fmt::Display for Version {
12 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
13 write!(f, "{0}.{1}.{2}", self.major, self.minor, self.build)
14 }
15}
16
17impl Version {
18 pub const fn new(major: u8, minor: u8, build: u8) -> Self {
19 Self {
20 major,
21 minor,
22 build,
23 }
24 }
25
26 pub fn parse(version: &str) -> Result<Self, VersionError> {
27 let parts: Vec<&str> = version.split('.').collect();
28
29 if parts.len() != 3 {
31 return Err(VersionError::InvalidSeparator(format!("Expected 2 '.' separators within version, got {}", parts.len() - 1)));
32 }
33
34 let major = parts[0].parse::<u8>().map_err(|_| VersionError::InvalidVersion)?;
36 let minor = parts[1].parse::<u8>().map_err(|_| VersionError::InvalidVersion)?;
37 let build = parts[2].parse::<u8>().map_err(|_| VersionError::InvalidVersion)?;
38
39 Ok(Version::new(major, minor, build))
41 }
42
43 pub fn is_compatible(&self, other: &Self) -> bool {
44 other.major == self.major && other.minor <= self.minor
45 }
46
47 pub fn is_compatible_or_else<F: FnOnce()>(&self, other: &Self, f: F) {
48 if !self.is_compatible(other) { f() }
49 }
50}
51
52pub mod error {
53 use std::{fmt, error::Error};
54
55 #[derive(Debug)]
56 pub enum VersionError {
57 InvalidSeparator(String),
58 InvalidVersion,
59 }
60
61 impl fmt::Display for VersionError {
62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63 match self {
64 VersionError::InvalidSeparator(s) => write!(f, "Invalid Version Separator '{}'", s),
65 VersionError::InvalidVersion => write!(f, "Invalid Version"),
66 }
67 }
68 }
69
70 impl Error for VersionError {}
71}