flex_version/
lib.rs

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
18/// The characters that are hard delimiters for components.
19/// This constant is public more as a matter of documentation than of utility.
20pub const COMPONENT_SEPARATORS: &str = ".-_+";
21
22/// A component is a indivisible part of a version.
23/// May be a number, or a alphabetic identifier.
24#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub enum Component {
26    Identifier(Box<str>),
27    Number(u32),
28}
29
30/// The default Component is the number zero.
31impl 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/// A parser for components.
53/// This is an iterator which will parse many components in succession.
54#[derive(Debug)]
55struct ComponentParser<'a> {
56    /// Flag to indicate whether we're parsing the first component.
57    first: bool,
58    /// The input yet to be parsed.
59    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 // Only allow separators after the first component.
79            .input
80            .strip_prefix(|c| COMPONENT_SEPARATORS.contains(c))
81        {
82            self.input = tail;
83        }
84        // Some versions have the format "7.4.1 (4452929)", so we must be able to
85        // parse the trailing parenthesized string.
86        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        // Try to parse a number.
96        if let Some(component) = self.parse_number() {
97            return Some(Ok(component));
98        }
99
100        // Try to parse an identifier.
101        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    /// Try to parse an integer of the given type.
111    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    /// Try to parse a number component.
123    fn parse_number(&mut self) -> Option<Component> {
124        self.parse_integer().map(Component::Number)
125    }
126
127    /// Try to parse an identifier component.
128    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    /// Generate a error with the current input.
138    fn error(&self) -> ParseVersionError {
139        ParseVersionError(self.input.to_owned())
140    }
141}
142
143/// An error while parsing a version.
144#[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/// A version. Versions are composed of one or more components, and provide a total
156/// ordering.
157#[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
215/// The default version is `0.0.0`.
216impl 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}