distant_net/common/
version.rs

1use semver::{Comparator, Op, Prerelease, Version as SemVer};
2use std::fmt;
3
4/// Represents a version and compatibility rules.
5#[derive(Clone, Debug)]
6pub struct Version {
7    inner: SemVer,
8    lower: Comparator,
9    upper: Comparator,
10}
11
12impl Version {
13    /// Creates a new version in the form `major.minor.patch` with a ruleset that is used to check
14    /// other versions such that `>=0.1.2, <0.2.0` or `>=1.2.3, <2` depending on whether or not the
15    /// major version is `0`.
16    ///
17    /// ```
18    /// use distant_net::common::Version;
19    ///
20    /// // Matching versions are compatible
21    /// let a = Version::new(1, 2, 3);
22    /// let b = Version::new(1, 2, 3);
23    /// assert!(a.is_compatible_with(&b));
24    ///
25    /// // Version 1.2.3 is compatible with 1.2.4, but not the other way
26    /// let a = Version::new(1, 2, 3);
27    /// let b = Version::new(1, 2, 4);
28    /// assert!(a.is_compatible_with(&b));
29    /// assert!(!b.is_compatible_with(&a));
30    ///
31    /// // Version 1.2.3 is compatible with 1.3.0, but not 2
32    /// let a = Version::new(1, 2, 3);
33    /// assert!(a.is_compatible_with(&Version::new(1, 3, 0)));
34    /// assert!(!a.is_compatible_with(&Version::new(2, 0, 0)));
35    ///
36    /// // Version 0.1.2 is compatible with 0.1.3, but not the other way
37    /// let a = Version::new(0, 1, 2);
38    /// let b = Version::new(0, 1, 3);
39    /// assert!(a.is_compatible_with(&b));
40    /// assert!(!b.is_compatible_with(&a));
41    ///
42    /// // Version 0.1.2 is not compatible with 0.2
43    /// let a = Version::new(0, 1, 2);
44    /// let b = Version::new(0, 2, 0);
45    /// assert!(!a.is_compatible_with(&b));
46    /// assert!(!b.is_compatible_with(&a));
47    /// ```
48    pub const fn new(major: u64, minor: u64, patch: u64) -> Self {
49        Self {
50            inner: SemVer::new(major, minor, patch),
51            lower: Comparator {
52                op: Op::GreaterEq,
53                major,
54                minor: Some(minor),
55                patch: Some(patch),
56                pre: Prerelease::EMPTY,
57            },
58            upper: Comparator {
59                op: Op::Less,
60                major: if major == 0 { 0 } else { major + 1 },
61                minor: if major == 0 { Some(minor + 1) } else { None },
62                patch: None,
63                pre: Prerelease::EMPTY,
64            },
65        }
66    }
67
68    /// Returns true if this version is compatible with another version.
69    pub fn is_compatible_with(&self, other: &Self) -> bool {
70        self.lower.matches(&other.inner) && self.upper.matches(&other.inner)
71    }
72
73    /// Converts from a collection of bytes into a version using the byte form major/minor/patch
74    /// using big endian.
75    pub const fn from_be_bytes(bytes: [u8; 24]) -> Self {
76        Self::new(
77            u64::from_be_bytes([
78                bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
79            ]),
80            u64::from_be_bytes([
81                bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14],
82                bytes[15],
83            ]),
84            u64::from_be_bytes([
85                bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22],
86                bytes[23],
87            ]),
88        )
89    }
90
91    /// Converts the version into a byte form of major/minor/patch using big endian.
92    pub const fn to_be_bytes(&self) -> [u8; 24] {
93        let major = self.inner.major.to_be_bytes();
94        let minor = self.inner.minor.to_be_bytes();
95        let patch = self.inner.patch.to_be_bytes();
96
97        [
98            major[0], major[1], major[2], major[3], major[4], major[5], major[6], major[7],
99            minor[0], minor[1], minor[2], minor[3], minor[4], minor[5], minor[6], minor[7],
100            patch[0], patch[1], patch[2], patch[3], patch[4], patch[5], patch[6], patch[7],
101        ]
102    }
103}
104
105impl Default for Version {
106    /// Default version is `0.0.0`.
107    fn default() -> Self {
108        Self::new(0, 0, 0)
109    }
110}
111
112impl fmt::Display for Version {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        write!(f, "{}", self.inner)
115    }
116}
117
118impl From<semver::Version> for Version {
119    /// Creates a new [`Version`] using the major, minor, and patch information from
120    /// [`semver::Version`].
121    fn from(version: semver::Version) -> Self {
122        let mut this = Self::new(version.major, version.minor, version.patch);
123        this.inner = version;
124        this
125    }
126}
127
128impl From<Version> for semver::Version {
129    fn from(version: Version) -> Self {
130        version.inner
131    }
132}