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