Skip to main content

kellnr_common/
version.rs

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