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}