Skip to main content

kellnr_common/
version.rs

1use std::cmp::Ordering;
2use std::convert::TryFrom;
3use std::fmt;
4use std::hash::{Hash, Hasher};
5use std::ops::Deref;
6
7use sea_orm::Value;
8use thiserror::Error;
9use utoipa::ToSchema;
10
11#[derive(Debug, Eq, Clone, serde::Serialize, ToSchema)]
12#[schema(value_type = String)]
13pub struct Version(String);
14
15impl<'de> serde::Deserialize<'de> for Version {
16    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
17    where
18        D: serde::Deserializer<'de>,
19    {
20        let s = <String as serde::Deserialize>::deserialize(deserializer)?;
21        semver::Version::parse(&s)
22            .map(|sv| Version(sv.to_string()))
23            .map_err(|e| serde::de::Error::custom(e.to_string()))
24    }
25}
26
27#[derive(Debug, Eq, PartialEq, Error)]
28pub enum VersionError {
29    #[error("Invalid SemVer")]
30    InvalidSemVer,
31}
32
33impl Version {
34    pub fn from_unchecked_str(version: &str) -> Self {
35        Self(version.to_string())
36    }
37    pub fn into_inner(self) -> String {
38        self.0
39    }
40}
41
42impl From<Version> for Value {
43    fn from(value: Version) -> Self {
44        Value::String(Some(value.0))
45    }
46}
47
48impl From<&Version> for Value {
49    fn from(value: &Version) -> Self {
50        Value::String(Some(value.0.clone()))
51    }
52}
53
54impl TryFrom<&str> for Version {
55    type Error = VersionError;
56
57    fn try_from(version: &str) -> Result<Self, Self::Error> {
58        Version::try_from(&version.to_string())
59    }
60}
61
62impl TryFrom<&String> for Version {
63    type Error = VersionError;
64
65    fn try_from(version: &String) -> Result<Self, Self::Error> {
66        match semver::Version::parse(version) {
67            Ok(sv) => Ok(Version(sv.to_string())),
68            Err(_) => Err(VersionError::InvalidSemVer),
69        }
70    }
71}
72
73impl AsRef<Version> for Version {
74    fn as_ref(&self) -> &Version {
75        self
76    }
77}
78
79impl Deref for Version {
80    type Target = String;
81
82    fn deref(&self) -> &Self::Target {
83        &self.0
84    }
85}
86
87impl Default for Version {
88    fn default() -> Self {
89        Version(semver::Version::new(0, 0, 0).to_string())
90    }
91}
92
93impl Ord for Version {
94    fn cmp(&self, other: &Self) -> Ordering {
95        let sv1 = semver::Version::parse(&self.to_string()).unwrap();
96        let sv2 = semver::Version::parse(&other.to_string()).unwrap();
97        sv1.cmp(&sv2)
98    }
99}
100
101impl PartialOrd for Version {
102    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
103        Some(self.cmp(other))
104    }
105}
106
107impl PartialEq for Version {
108    fn eq(&self, other: &Self) -> bool {
109        let sv1 = semver::Version::parse(&self.to_string()).unwrap();
110        let sv2 = semver::Version::parse(&other.to_string()).unwrap();
111        sv1 == sv2
112    }
113}
114
115impl Hash for Version {
116    fn hash<H: Hasher>(&self, state: &mut H) {
117        let sv = semver::Version::parse(&self.to_string()).unwrap();
118        sv.hash(state);
119    }
120}
121
122impl fmt::Display for Version {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        write!(f, "{}", self.0)
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn valid_package_versions() {
134        assert_eq!(Version::try_from("0.0.0").unwrap().to_string(), "0.0.0");
135        assert_eq!(Version::try_from("1.0.1").unwrap().to_string(), "1.0.1");
136        assert_eq!(
137            Version::try_from("23.123.343").unwrap().to_string(),
138            "23.123.343"
139        );
140        assert_eq!(
141            Version::try_from("2.43.3-alpha34").unwrap().to_string(),
142            "2.43.3-alpha34"
143        );
144        assert_eq!(
145            Version::try_from("0.1.1-45rdfsd-45").unwrap().to_string(),
146            "0.1.1-45rdfsd-45"
147        );
148    }
149
150    #[test]
151    fn invalid_package_versions() {
152        assert_eq!(
153            Version::try_from("a.1.2").unwrap_err(),
154            VersionError::InvalidSemVer
155        );
156        assert_eq!(
157            Version::try_from("002.23.1").unwrap_err(),
158            VersionError::InvalidSemVer
159        );
160        assert_eq!(
161            Version::try_from("3.2fg.3").unwrap_err(),
162            VersionError::InvalidSemVer
163        );
164        assert_eq!(
165            Version::try_from("5.3.2.3").unwrap_err(),
166            VersionError::InvalidSemVer
167        );
168    }
169}