1use std::fmt;
19use std::str::FromStr;
20
21use regex::Regex;
22
23use super::VersionError;
24
25#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub enum Version {
34 Pre10(u32, u32, u32),
37 Post10(u32, u32),
40}
41
42impl fmt::Display for Version {
43 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
44 match self {
45 Version::Pre10(a, b, c) => fmt.pad(&format!("{a}.{b}.{c}")),
46 Version::Post10(a, b) => fmt.pad(&format!("{a}.{b}")),
47 }
48 }
49}
50
51impl FromStr for Version {
52 type Err = VersionError;
53
54 fn from_str(s: &str) -> Result<Self, Self::Err> {
55 lazy_static! {
56 static ref RE: Regex = Regex::new(r"(?x) \b (\d+) [.] (\d+) (?: [.] (\d+) )? \b")
57 .expect("invalid regex (for matching PostgreSQL versions)");
58 }
59 let badly_formed = |_| VersionError::BadlyFormed { text: Some(s.into()) };
60 match RE.captures(s) {
61 Some(caps) => {
62 let a = caps[1].parse::<u32>().map_err(badly_formed)?;
63 let b = caps[2].parse::<u32>().map_err(badly_formed)?;
64 match caps.get(3) {
65 None if a >= 10 => Ok(Version::Post10(a, b)),
66 None => Err(VersionError::BadlyFormed { text: Some(s.into()) }),
67 Some(_) if a >= 10 => Err(VersionError::BadlyFormed { text: Some(s.into()) }),
68 Some(m) => Ok(m
69 .as_str()
70 .parse::<u32>()
71 .map(|c| Version::Pre10(a, b, c))
72 .map_err(badly_formed)?),
73 }
74 }
75 None => Err(VersionError::NotFound { text: Some(s.into()) }),
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::Version::{Post10, Pre10};
83 use super::{Version, VersionError::*};
84
85 use std::cmp::Ordering;
86
87 #[test]
88 fn parses_version_below_10() {
89 assert_eq!(Ok(Pre10(9, 6, 17)), "9.6.17".parse());
90 }
91
92 #[test]
93 fn parses_version_above_10() {
94 assert_eq!(Ok(Post10(12, 2)), "12.2".parse());
95 }
96
97 #[test]
98 fn parse_returns_error_when_version_is_invalid() {
99 assert!(matches!(
101 "4294967296.0".parse::<Version>(),
102 Err(BadlyFormed { .. })
103 ));
104 }
105
106 #[test]
107 fn parse_returns_error_when_version_not_found() {
108 assert!(matches!("foo".parse::<Version>(), Err(NotFound { .. })));
109 }
110
111 #[test]
112 fn displays_version_below_10() {
113 assert_eq!("9.6.17", format!("{}", Pre10(9, 6, 17)));
114 }
115
116 #[test]
117 fn displays_version_above_10() {
118 assert_eq!("12.2", format!("{}", Post10(12, 2)));
119 }
120
121 #[test]
122 #[rustfmt::skip]
123 fn derive_partial_ord_works_as_expected() {
124 assert_eq!(Pre10(9, 10, 11).partial_cmp(&Post10(10, 11)), Some(Ordering::Less));
125 assert_eq!(Post10(10, 11).partial_cmp(&Pre10(9, 10, 11)), Some(Ordering::Greater));
126 assert_eq!(Pre10(9, 10, 11).partial_cmp(&Pre10(9, 10, 11)), Some(Ordering::Equal));
127 assert_eq!(Post10(10, 11).partial_cmp(&Post10(10, 11)), Some(Ordering::Equal));
128 }
129
130 #[test]
131 fn derive_ord_works_as_expected() {
132 let mut versions = vec![
133 Pre10(9, 10, 11),
134 Post10(10, 11),
135 Post10(14, 2),
136 Pre10(9, 10, 12),
137 Post10(10, 12),
138 ];
139 versions.sort(); assert_eq!(
141 versions,
142 vec![
143 Pre10(9, 10, 11),
144 Pre10(9, 10, 12),
145 Post10(10, 11),
146 Post10(10, 12),
147 Post10(14, 2)
148 ]
149 );
150 }
151}