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}