1use serde::de::{Error as DeError, Visitor};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::fmt;
4use std::str::FromStr;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct SemVer {
11 pub major: u32,
12 pub minor: u32,
13 pub patch: u32,
14}
15
16impl fmt::Display for SemVer {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
18 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
19 }
20}
21
22impl FromStr for SemVer {
23 type Err = &'static str;
24
25 fn from_str(s: &str) -> Result<Self, Self::Err> {
26 const ERR: &str = "Failed to parse version string. Expected: major.minor.patch";
27 let mut pieces = s.split('.');
28 let major = u32::from_str(pieces.next().ok_or(ERR)?).map_err(|_| ERR)?;
29 let minor = u32::from_str(pieces.next().ok_or(ERR)?).map_err(|_| ERR)?;
30 let patch = u32::from_str(pieces.next().ok_or(ERR)?).map_err(|_| ERR)?;
31 Ok(SemVer {
32 major,
33 minor,
34 patch,
35 })
36 }
37}
38
39impl Default for SemVer {
40 fn default() -> Self {
42 Self {
43 major: 0,
44 minor: 1,
45 patch: 0,
46 }
47 }
48}
49
50impl Serialize for SemVer {
51 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
52 where
53 S: Serializer,
54 {
55 let as_str = self.to_string();
56 serializer.serialize_str(&as_str)
57 }
58}
59
60struct SemVerVisitor;
61
62impl<'de> Visitor<'de> for SemVerVisitor {
63 type Value = SemVer;
64 fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
65 write!(fmt, "a string of the form \"major.minor.patch\"")
66 }
67
68 fn visit_str<E>(self, v: &str) -> Result<SemVer, E>
69 where
70 E: DeError,
71 {
72 SemVer::from_str(v).map_err(DeError::custom)
73 }
74
75 fn visit_string<E>(self, v: String) -> Result<SemVer, E>
76 where
77 E: DeError,
78 {
79 SemVer::from_str(&v).map_err(DeError::custom)
80 }
81}
82
83impl<'de> Deserialize<'de> for SemVer {
84 fn deserialize<D>(deserializer: D) -> Result<SemVer, D::Error>
85 where
86 D: Deserializer<'de>,
87 {
88 deserializer.deserialize_string(SemVerVisitor)
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn semver_to_string() {
98 let version = SemVer {
99 major: 1,
100 minor: 2,
101 patch: 3,
102 };
103 assert_eq!(version.to_string(), "1.2.3");
104 }
105
106 #[test]
107 fn semver_from_string() {
108 let version = "1.1.23";
109 let v = SemVer::from_str(&version);
110 assert!(v.is_ok(), "{}", v.unwrap_err());
111 assert_eq!(
112 v.unwrap(),
113 SemVer {
114 major: 1,
115 minor: 1,
116 patch: 23
117 }
118 );
119 }
120
121 #[test]
122 fn semver_deserialize() {
123 let version = r#""1.1.23""#;
124 let v: Result<SemVer, _> = serde_json::from_str(version);
125 assert!(v.is_ok(), "{}", v.unwrap_err());
126 assert_eq!(
127 v.unwrap(),
128 SemVer {
129 major: 1,
130 minor: 1,
131 patch: 23
132 }
133 );
134 }
135
136 #[test]
137 fn semver_serialize() {
138 let version = SemVer {
139 major: 1,
140 minor: 1,
141 patch: 23,
142 };
143 let s = serde_json::to_string(&version);
144 assert!(s.is_ok(), "{}", s.unwrap_err());
145 assert_eq!(s.unwrap(), r#""1.1.23""#);
146 }
147
148 #[test]
149 fn semver_invalid_strings() {
150 let invalid = ["1.23", "1.", "1", "asdf", "a.b.c"];
151 for version in invalid {
152 let v = SemVer::from_str(&version);
153 assert!(v.is_err());
154 }
155 }
156
157 #[test]
158 fn semver_parsing() {
159 let version = "1.0.0".parse::<SemVer>();
160 assert!(version.is_ok());
161 assert_eq!(
162 version.unwrap(),
163 SemVer {
164 major: 1,
165 minor: 0,
166 patch: 0
167 }
168 );
169 let version = "asdf".parse::<SemVer>();
170 assert!(version.is_err());
171 let version = "v1.0.3".parse::<SemVer>();
172 assert!(version.is_err());
173 let version = "1.0".parse::<SemVer>();
174 assert!(version.is_err());
175 let version = "1".parse::<SemVer>();
176 assert!(version.is_err());
177 let version = "1.0.-10".parse::<SemVer>();
178 assert!(version.is_err());
179 }
180
181 #[test]
182 fn semver_default() {
183 let v1 = SemVer::default();
184 assert_eq!(
185 v1,
186 SemVer {
187 major: 0,
188 minor: 1,
189 patch: 0
190 }
191 );
192 assert_eq!(v1, "0.1.0".parse().unwrap());
193 }
194}