1#![doc = include_str!("../README.md")]
2
3#[cfg(feature = "serde")]
4mod serde;
5#[cfg(test)]
6mod tests;
7mod util;
8
9use std::{
10 cmp::Ordering,
11 error::Error,
12 fmt::{self, Debug, Display},
13 str::FromStr,
14};
15
16use util::SplitPrefix;
17
18pub const COMPONENT_SEPARATORS: &str = ".-_+";
21
22#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub enum Component {
26 Identifier(Box<str>),
27 Number(u32),
28}
29
30impl Default for Component {
32 fn default() -> Self {
33 Self::Number(0)
34 }
35}
36
37impl Display for Component {
38 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39 match self {
40 Component::Number(number) => write!(f, "{}", number),
41 Component::Identifier(id) => f.write_str(id),
42 }
43 }
44}
45
46impl Debug for Component {
47 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48 Display::fmt(self, f)
49 }
50}
51
52#[derive(Debug)]
55struct ComponentParser<'a> {
56 first: bool,
58 input: &'a str,
60}
61
62impl<'a> From<&'a str> for ComponentParser<'a> {
63 fn from(input: &'a str) -> Self {
64 Self { first: true, input }
65 }
66}
67
68impl<'a> Iterator for ComponentParser<'a> {
69 type Item = Result<Component, ParseVersionError>;
70
71 fn next(&mut self) -> Option<Self::Item> {
72 if self.input.is_empty() {
73 return None;
74 }
75
76 if self.first {
77 self.first = false;
78 } else if let Some(tail) = self .input
80 .strip_prefix(|c| COMPONENT_SEPARATORS.contains(c))
81 {
82 self.input = tail;
83 }
84 else if let Some(tail) = self.input.strip_prefix(" (") {
87 return if tail.trim_start_matches(|c| c != ')') == ")" {
88 self.input = "";
89 None
90 } else {
91 Some(Err(self.error()))
92 };
93 }
94
95 if let Some(component) = self.parse_number() {
97 return Some(Ok(component));
98 }
99
100 if let Some(component) = self.parse_identifier() {
102 return Some(Ok(component));
103 }
104
105 Some(Err(self.error()))
106 }
107}
108
109impl<'a> ComponentParser<'a> {
110 fn parse_integer<N: FromStr>(&mut self) -> Option<N> {
112 if let Some((integer, tail)) = self.input.split_prefix(|c| c.is_ascii_digit()) {
113 if let Ok(integer) = integer.parse() {
114 self.input = tail;
115 return Some(integer);
116 }
117 }
118
119 None
120 }
121
122 fn parse_number(&mut self) -> Option<Component> {
124 self.parse_integer().map(Component::Number)
125 }
126
127 fn parse_identifier(&mut self) -> Option<Component> {
129 if let Some((identifier, tail)) = self.input.split_prefix(|c| c.is_ascii_alphabetic()) {
130 self.input = tail;
131 return Some(Component::Identifier(identifier.into()));
132 }
133
134 None
135 }
136
137 fn error(&self) -> ParseVersionError {
139 ParseVersionError(self.input.to_owned())
140 }
141}
142
143#[derive(Debug, Clone)]
145pub struct ParseVersionError(String);
146
147impl Display for ParseVersionError {
148 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149 write!(f, "invalid version: {}", self.0)
150 }
151}
152
153impl Error for ParseVersionError {}
154
155#[derive(Clone)]
158pub struct Version(Box<[Component]>);
159
160impl PartialEq for Version {
161 fn eq(&self, other: &Self) -> bool {
162 let mut self_iter = self.0.iter().fuse();
163 let mut other_iter = other.0.iter().fuse();
164
165 loop {
166 match (self_iter.next(), other_iter.next()) {
167 (None, None) => return true,
168
169 (None, Some(Component::Number(0))) => continue,
170 (Some(Component::Number(0)), None) => continue,
171
172 (None, Some(_)) => return false,
173 (Some(_), None) => return false,
174
175 (Some(c1), Some(c2)) if c1 == c2 => continue,
176 (Some(_), Some(_)) => return false,
177 }
178 }
179 }
180}
181
182impl Eq for Version {}
183
184impl PartialOrd for Version {
185 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
186 Some(self.cmp(other))
187 }
188}
189
190impl Ord for Version {
191 fn cmp(&self, other: &Self) -> Ordering {
192 let mut self_iter = self.0.iter().fuse();
193 let mut other_iter = other.0.iter().fuse();
194
195 loop {
196 match (self_iter.next(), other_iter.next()) {
197 (None, None) => return Ordering::Equal,
198
199 (None, Some(Component::Number(0))) => continue,
200 (Some(Component::Number(0)), None) => continue,
201
202 (None, Some(Component::Number(_))) => return Ordering::Less,
203 (Some(Component::Number(_)), None) => return Ordering::Greater,
204
205 (None, Some(Component::Identifier(_))) => return Ordering::Greater,
206 (Some(Component::Identifier(_)), None) => return Ordering::Less,
207
208 (Some(c1), Some(c2)) if c1.cmp(c2) == Ordering::Equal => continue,
209 (Some(c1), Some(c2)) => return c1.cmp(c2),
210 }
211 }
212 }
213}
214
215impl Default for Version {
217 fn default() -> Self {
218 Self(vec![Component::default(); 3].into_boxed_slice())
219 }
220}
221
222impl FromStr for Version {
223 type Err = ParseVersionError;
224
225 fn from_str(input: &str) -> Result<Self, Self::Err> {
226 ComponentParser::from(input)
227 .collect::<Result<_, _>>()
228 .map(Self)
229 }
230}
231
232impl Display for Version {
233 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
234 let mut iterator = self.0.iter();
235
236 if let Some(first) = iterator.next() {
237 write!(f, "{}", first)?;
238 }
239
240 for component in iterator {
241 write!(f, ".{}", component)?;
242 }
243
244 Ok(())
245 }
246}
247
248impl Debug for Version {
249 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250 Display::fmt(self, f)
251 }
252}