northstar_runtime/common/
version.rs1use serde::{Deserialize, Serialize};
2use std::{
3 cmp::Ordering,
4 fmt::{self, Display},
5 str::FromStr,
6};
7use thiserror::Error;
8
9#[derive(Error, Debug)]
11pub struct ParseError {
12 #[from]
13 source: semver::Error,
14}
15
16impl fmt::Display for ParseError {
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 write!(f, "{}", self.source)
19 }
20}
21
22#[derive(Clone, Eq, PartialEq, Hash, Debug)]
24pub struct Version {
25 pub major: u64,
27 pub minor: u64,
29 pub patch: u64,
31}
32
33impl Version {
34 pub const fn new(major: u64, minor: u64, patch: u64) -> Self {
36 Self {
37 major,
38 minor,
39 patch,
40 }
41 }
42
43 pub fn parse(version: &str) -> Result<Self, ParseError> {
45 semver::Version::parse(version)
46 .map(|ref version| version.into())
47 .map_err(|source| ParseError { source })
48 }
49}
50
51impl fmt::Display for Version {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
54 }
55}
56
57impl From<&semver::Version> for Version {
58 fn from(version: &semver::Version) -> Self {
59 Self {
60 major: version.major,
61 minor: version.minor,
62 patch: version.patch,
63 }
64 }
65}
66
67impl From<&Version> for semver::Version {
68 fn from(version: &Version) -> Self {
69 semver::Version::new(version.major, version.minor, version.patch)
70 }
71}
72
73impl<T> From<(T, T, T)> for Version
74where
75 T: Into<u64>,
76{
77 fn from((major, minor, patch): (T, T, T)) -> Self {
78 Self {
79 major: major.into(),
80 minor: minor.into(),
81 patch: patch.into(),
82 }
83 }
84}
85
86impl FromStr for Version {
87 type Err = ParseError;
88
89 fn from_str(s: &str) -> Result<Self, Self::Err> {
90 semver::Version::parse(s)
91 .map(|ref version| version.into())
92 .map_err(|source| ParseError { source })
93 }
94}
95
96impl Serialize for Version {
97 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
98 where
99 S: serde::Serializer,
100 {
101 serializer.serialize_str(&self.to_string())
102 }
103}
104
105impl<'de> Deserialize<'de> for Version {
106 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
107 where
108 D: serde::Deserializer<'de>,
109 {
110 let s = String::deserialize(deserializer)?;
111 Version::from_str(&s).map_err(serde::de::Error::custom)
112 }
113}
114
115impl PartialOrd for Version {
116 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
117 Some(self.cmp(other))
118 }
119}
120
121impl Ord for Version {
122 fn cmp(&self, other: &Self) -> Ordering {
123 if self.major > other.major {
124 Ordering::Greater
125 } else if self.major < other.major {
126 Ordering::Less
127 } else if self.minor > other.minor {
128 Ordering::Greater
129 } else if self.minor < other.minor {
130 Ordering::Less
131 } else if self.patch > other.patch {
132 Ordering::Greater
133 } else if self.patch < other.patch {
134 Ordering::Less
135 } else {
136 Ordering::Equal
137 }
138 }
139}
140
141#[derive(Clone, Eq, PartialEq, Hash, Debug)]
143pub struct VersionReq {
144 inner: semver::VersionReq,
145}
146
147impl VersionReq {
148 pub fn parse(text: &str) -> Result<VersionReq, ParseError> {
150 Ok(VersionReq {
151 inner: semver::VersionReq::parse(text)?,
152 })
153 }
154
155 pub fn matches(&self, version: &Version) -> bool {
157 self.inner.matches(&semver::Version::from(version))
158 }
159}
160
161impl FromStr for VersionReq {
162 type Err = ParseError;
163
164 fn from_str(s: &str) -> Result<Self, Self::Err> {
165 Ok(VersionReq {
166 inner: semver::VersionReq::from_str(s)?,
167 })
168 }
169}
170
171impl Display for VersionReq {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 write!(f, "{}", self.inner)
174 }
175}
176
177impl Serialize for VersionReq {
178 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
179 where
180 S: serde::Serializer,
181 {
182 serializer.serialize_str(&self.inner.to_string())
183 }
184}
185
186impl<'de> Deserialize<'de> for VersionReq {
187 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
188 where
189 D: serde::Deserializer<'de>,
190 {
191 let s = String::deserialize(deserializer)?;
192 VersionReq::parse(&s).map_err(serde::de::Error::custom)
193 }
194}
195
196#[test]
197fn version() -> anyhow::Result<()> {
198 let v1 = Version::parse("1.0.0")?;
199 let v2 = Version::parse("2.0.0")?;
200 let v3 = Version::parse("3.0.0")?;
201 assert!(v2 > v1);
202 assert!(v2 < v3);
203 let v1_1 = Version::parse("1.1.0")?;
204 assert!(v1_1 > v1);
205 let v1_1_1 = Version::parse("1.1.1")?;
206 assert!(v1_1_1 > v1_1);
207 Ok(())
208}