Skip to main content

borderless_pkg/
semver.rs

1use serde::de::{Error as DeError, Visitor};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::fmt;
4use std::str::FromStr;
5
6/// Semantic version
7///
8/// Is serialized as "major.minor.patch".
9#[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    /// Initializes version with "0.1.0"
41    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}