version_compare/
cmp.rs

1//! Module with all supported comparison operators.
2//!
3//! This module provides an enum with all comparison operators that can be used with this library.
4//! The enum provides various useful helper functions to inverse or flip an operator.
5//!
6//! Methods like `Cmp::from_sign(">");` can be used to get a comparison operator by it's logical
7//! sign from a string.
8
9use std::cmp::Ordering;
10
11/// Comparison operators enum.
12#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
13pub enum Cmp {
14    /// Equal (`==`, `=`).
15    /// When version `A` is equal to `B`.
16    Eq,
17
18    /// Not equal (`!=`, `!`, `<>`).
19    /// When version `A` is not equal to `B`.
20    Ne,
21
22    /// Less than (`<`).
23    /// When version `A` is less than `B` but not equal.
24    Lt,
25
26    /// Less or equal (`<=`).
27    /// When version `A` is less than or equal to `B`.
28    Le,
29
30    /// Greater or equal (`>=`).
31    /// When version `A` is greater than or equal to `B`.
32    Ge,
33
34    /// Greater than (`>`).
35    /// When version `A` is greater than `B` but not equal.
36    Gt,
37}
38
39impl Cmp {
40    /// Get a comparison operator by it's sign.
41    /// Whitespaces are stripped from the sign string.
42    /// An error is returned if the sign isn't recognized.
43    ///
44    /// The following signs are supported:
45    ///
46    /// * `==` _or_ `=` -> `Eq`
47    /// * `!=` _or_ `!` _or_ `<>` -> `Ne`
48    /// * `< ` -> `Lt`
49    /// * `<=` -> `Le`
50    /// * `>=` -> `Ge`
51    /// * `> ` -> `Gt`
52    ///
53    /// # Examples
54    ///
55    /// ```
56    /// use version_compare::Cmp;
57    ///
58    /// assert_eq!(Cmp::from_sign("=="), Ok(Cmp::Eq));
59    /// assert_eq!(Cmp::from_sign("<"), Ok(Cmp::Lt));
60    /// assert_eq!(Cmp::from_sign("  >=   "), Ok(Cmp::Ge));
61    /// assert!(Cmp::from_sign("*").is_err());
62    /// ```
63    #[allow(clippy::result_unit_err)]
64    pub fn from_sign<S: AsRef<str>>(sign: S) -> Result<Cmp, ()> {
65        match sign.as_ref().trim() {
66            "==" | "=" => Ok(Cmp::Eq),
67            "!=" | "!" | "<>" => Ok(Cmp::Ne),
68            "<" => Ok(Cmp::Lt),
69            "<=" => Ok(Cmp::Le),
70            ">=" => Ok(Cmp::Ge),
71            ">" => Ok(Cmp::Gt),
72            _ => Err(()),
73        }
74    }
75
76    /// Get a comparison operator by it's name.
77    /// Names are case-insensitive, and whitespaces are stripped from the string.
78    /// An error is returned if the name isn't recognized.
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// use version_compare::Cmp;
84    ///
85    /// assert_eq!(Cmp::from_name("eq"), Ok(Cmp::Eq));
86    /// assert_eq!(Cmp::from_name("lt"), Ok(Cmp::Lt));
87    /// assert_eq!(Cmp::from_name("  Ge   "), Ok(Cmp::Ge));
88    /// assert!(Cmp::from_name("abc").is_err());
89    /// ```
90    #[allow(clippy::result_unit_err)]
91    pub fn from_name<S: AsRef<str>>(sign: S) -> Result<Cmp, ()> {
92        match sign.as_ref().trim().to_lowercase().as_str() {
93            "eq" => Ok(Cmp::Eq),
94            "ne" => Ok(Cmp::Ne),
95            "lt" => Ok(Cmp::Lt),
96            "le" => Ok(Cmp::Le),
97            "ge" => Ok(Cmp::Ge),
98            "gt" => Ok(Cmp::Gt),
99            _ => Err(()),
100        }
101    }
102
103    /// Get the comparison operator from Rusts `Ordering` enum.
104    ///
105    /// The following comparison operators are returned:
106    ///
107    /// * `Ordering::Less` -> `Lt`
108    /// * `Ordering::Equal` -> `Eq`
109    /// * `Ordering::Greater` -> `Gt`
110    #[deprecated(since = "0.2.0", note = "use Cmp::from(ord) instead")]
111    pub fn from_ord(ord: Ordering) -> Cmp {
112        Self::from(ord)
113    }
114
115    /// Get the name of this comparison operator.
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use version_compare::Cmp;
121    ///
122    /// assert_eq!(Cmp::Eq.name(), "eq");
123    /// assert_eq!(Cmp::Lt.name(), "lt");
124    /// assert_eq!(Cmp::Ge.name(), "ge");
125    /// ```
126    pub fn name<'a>(self) -> &'a str {
127        match self {
128            Cmp::Eq => "eq",
129            Cmp::Ne => "ne",
130            Cmp::Lt => "lt",
131            Cmp::Le => "le",
132            Cmp::Ge => "ge",
133            Cmp::Gt => "gt",
134        }
135    }
136
137    /// Get the inverted comparison operator.
138    ///
139    /// This uses the following bidirectional rules:
140    ///
141    /// * `Eq` <-> `Ne`
142    /// * `Lt` <-> `Ge`
143    /// * `Le` <-> `Gt`
144    ///
145    /// # Examples
146    ///
147    /// ```
148    /// use version_compare::Cmp;
149    ///
150    /// assert_eq!(Cmp::Eq.invert(), Cmp::Ne);
151    /// assert_eq!(Cmp::Lt.invert(), Cmp::Ge);
152    /// assert_eq!(Cmp::Gt.invert(), Cmp::Le);
153    /// ```
154    #[must_use]
155    pub fn invert(self) -> Self {
156        match self {
157            Cmp::Eq => Cmp::Ne,
158            Cmp::Ne => Cmp::Eq,
159            Cmp::Lt => Cmp::Ge,
160            Cmp::Le => Cmp::Gt,
161            Cmp::Ge => Cmp::Lt,
162            Cmp::Gt => Cmp::Le,
163        }
164    }
165
166    /// Get the opposite comparison operator.
167    ///
168    /// This uses the following bidirectional rules:
169    ///
170    /// * `Eq` <-> `Ne`
171    /// * `Lt` <-> `Gt`
172    /// * `Le` <-> `Ge`
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// use version_compare::Cmp;
178    ///
179    /// assert_eq!(Cmp::Eq.opposite(), Cmp::Ne);
180    /// assert_eq!(Cmp::Lt.opposite(), Cmp::Gt);
181    /// assert_eq!(Cmp::Ge.opposite(), Cmp::Le);
182    /// ```
183    #[must_use]
184    pub fn opposite(self) -> Self {
185        match self {
186            Cmp::Eq => Cmp::Ne,
187            Cmp::Ne => Cmp::Eq,
188            Cmp::Lt => Cmp::Gt,
189            Cmp::Le => Cmp::Ge,
190            Cmp::Ge => Cmp::Le,
191            Cmp::Gt => Cmp::Lt,
192        }
193    }
194
195    /// Get the flipped comparison operator.
196    ///
197    /// This uses the following bidirectional rules:
198    ///
199    /// * `Lt` <-> `Gt`
200    /// * `Le` <-> `Ge`
201    /// * Other operators are returned as is.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// use version_compare::Cmp;
207    ///
208    /// assert_eq!(Cmp::Eq.flip(), Cmp::Eq);
209    /// assert_eq!(Cmp::Lt.flip(), Cmp::Gt);
210    /// assert_eq!(Cmp::Ge.flip(), Cmp::Le);
211    /// ```
212    #[must_use]
213    pub fn flip(self) -> Self {
214        match self {
215            Cmp::Lt => Cmp::Gt,
216            Cmp::Le => Cmp::Ge,
217            Cmp::Ge => Cmp::Le,
218            Cmp::Gt => Cmp::Lt,
219            _ => self,
220        }
221    }
222
223    /// Get the sign for this comparison operator.
224    ///
225    /// The following signs are returned:
226    ///
227    /// * `Eq` -> `==`
228    /// * `Ne` -> `!=`
229    /// * `Lt` -> `< `
230    /// * `Le` -> `<=`
231    /// * `Ge` -> `>=`
232    /// * `Gt` -> `> `
233    ///
234    /// Note: Some comparison operators also support other signs,
235    /// such as `=` for `Eq` and `!` for `Ne`,
236    /// these are never returned by this method however as the table above is used.
237    ///
238    /// # Examples
239    ///
240    /// ```
241    /// use version_compare::Cmp;
242    ///
243    /// assert_eq!(Cmp::Eq.sign(), "==");
244    /// assert_eq!(Cmp::Lt.sign(), "<");
245    /// assert_eq!(Cmp::Ge.flip().sign(), "<=");
246    /// ```
247    pub fn sign(self) -> &'static str {
248        match self {
249            Cmp::Eq => "==",
250            Cmp::Ne => "!=",
251            Cmp::Lt => "<",
252            Cmp::Le => "<=",
253            Cmp::Ge => ">=",
254            Cmp::Gt => ">",
255        }
256    }
257
258    /// Get a factor (number) for this comparison operator.
259    /// These factors can be useful for quick calculations.
260    ///
261    /// The following factor numbers are returned:
262    ///
263    /// * `Eq` _or_ `Ne` -> ` 0`
264    /// * `Lt` _or_ `Le` -> `-1`
265    /// * `Gt` _or_ `Ge` -> ` 1`
266    ///
267    /// # Examples
268    ///
269    /// ```
270    /// use version_compare::Version;
271    ///
272    /// let a = Version::from("1.2.3").unwrap();
273    /// let b = Version::from("1.3").unwrap();
274    ///
275    /// assert_eq!(a.compare(&b).factor(), -1);
276    /// assert_eq!(10 * b.compare(a).factor(), 10);
277    /// ```
278    pub fn factor(self) -> i8 {
279        match self {
280            Cmp::Eq | Cmp::Ne => 0,
281            Cmp::Lt | Cmp::Le => -1,
282            Cmp::Gt | Cmp::Ge => 1,
283        }
284    }
285
286    /// Get Rust's ordering for this comparison operator.
287    ///
288    /// The following comparison operators are supported:
289    ///
290    /// * `Eq` -> `Ordering::Equal`
291    /// * `Lt` -> `Ordering::Less`
292    /// * `Gt` -> `Ordering::Greater`
293    ///
294    /// For other comparison operators `None` is returned.
295    ///
296    /// # Examples
297    ///
298    /// ```
299    /// use std::cmp::Ordering;
300    /// use version_compare::Version;
301    ///
302    /// let a = Version::from("1.2.3").unwrap();
303    /// let b = Version::from("1.3").unwrap();
304    ///
305    /// assert_eq!(a.compare(b).ord().unwrap(), Ordering::Less);
306    /// ```
307    pub fn ord(self) -> Option<Ordering> {
308        match self {
309            Cmp::Eq => Some(Ordering::Equal),
310            Cmp::Lt => Some(Ordering::Less),
311            Cmp::Gt => Some(Ordering::Greater),
312            _ => None,
313        }
314    }
315}
316
317impl From<Ordering> for Cmp {
318    /// Get the comparison operator from Rusts `Ordering` enum.
319    ///
320    /// The following comparison operators are returned:
321    ///
322    /// * `Ordering::Less` -> `Lt`
323    /// * `Ordering::Equal` -> `Eq`
324    /// * `Ordering::Greater` -> `Gt`
325    fn from(ord: Ordering) -> Self {
326        match ord {
327            Ordering::Less => Cmp::Lt,
328            Ordering::Equal => Cmp::Eq,
329            Ordering::Greater => Cmp::Gt,
330        }
331    }
332}
333
334#[cfg_attr(tarpaulin, skip)]
335#[cfg(test)]
336mod tests {
337    use std::cmp::Ordering;
338
339    use super::Cmp;
340
341    #[test]
342    fn from_sign() {
343        // Normal signs
344        assert_eq!(Cmp::from_sign("==").unwrap(), Cmp::Eq);
345        assert_eq!(Cmp::from_sign("=").unwrap(), Cmp::Eq);
346        assert_eq!(Cmp::from_sign("!=").unwrap(), Cmp::Ne);
347        assert_eq!(Cmp::from_sign("!").unwrap(), Cmp::Ne);
348        assert_eq!(Cmp::from_sign("<>").unwrap(), Cmp::Ne);
349        assert_eq!(Cmp::from_sign("<").unwrap(), Cmp::Lt);
350        assert_eq!(Cmp::from_sign("<=").unwrap(), Cmp::Le);
351        assert_eq!(Cmp::from_sign(">=").unwrap(), Cmp::Ge);
352        assert_eq!(Cmp::from_sign(">").unwrap(), Cmp::Gt);
353
354        // Exceptional cases
355        assert_eq!(Cmp::from_sign("  <=  ").unwrap(), Cmp::Le);
356        assert_eq!(Cmp::from_sign("*"), Err(()));
357    }
358
359    #[test]
360    fn from_name() {
361        // Normal names
362        assert_eq!(Cmp::from_name("eq").unwrap(), Cmp::Eq);
363        assert_eq!(Cmp::from_name("ne").unwrap(), Cmp::Ne);
364        assert_eq!(Cmp::from_name("lt").unwrap(), Cmp::Lt);
365        assert_eq!(Cmp::from_name("le").unwrap(), Cmp::Le);
366        assert_eq!(Cmp::from_name("ge").unwrap(), Cmp::Ge);
367        assert_eq!(Cmp::from_name("gt").unwrap(), Cmp::Gt);
368
369        // Exceptional cases
370        assert_eq!(Cmp::from_name("  Le  ").unwrap(), Cmp::Le);
371        assert_eq!(Cmp::from_name("abc"), Err(()));
372    }
373
374    #[test]
375    fn from_ord() {
376        assert_eq!(Cmp::from(Ordering::Less), Cmp::Lt);
377        assert_eq!(Cmp::from(Ordering::Equal), Cmp::Eq);
378        assert_eq!(Cmp::from(Ordering::Greater), Cmp::Gt);
379    }
380
381    #[test]
382    fn name() {
383        assert_eq!(Cmp::Eq.name(), "eq");
384        assert_eq!(Cmp::Ne.name(), "ne");
385        assert_eq!(Cmp::Lt.name(), "lt");
386        assert_eq!(Cmp::Le.name(), "le");
387        assert_eq!(Cmp::Ge.name(), "ge");
388        assert_eq!(Cmp::Gt.name(), "gt");
389    }
390
391    #[test]
392    fn invert() {
393        assert_eq!(Cmp::Ne.invert(), Cmp::Eq);
394        assert_eq!(Cmp::Eq.invert(), Cmp::Ne);
395        assert_eq!(Cmp::Ge.invert(), Cmp::Lt);
396        assert_eq!(Cmp::Gt.invert(), Cmp::Le);
397        assert_eq!(Cmp::Lt.invert(), Cmp::Ge);
398        assert_eq!(Cmp::Le.invert(), Cmp::Gt);
399    }
400
401    #[test]
402    fn opposite() {
403        assert_eq!(Cmp::Eq.opposite(), Cmp::Ne);
404        assert_eq!(Cmp::Ne.opposite(), Cmp::Eq);
405        assert_eq!(Cmp::Lt.opposite(), Cmp::Gt);
406        assert_eq!(Cmp::Le.opposite(), Cmp::Ge);
407        assert_eq!(Cmp::Ge.opposite(), Cmp::Le);
408        assert_eq!(Cmp::Gt.opposite(), Cmp::Lt);
409    }
410
411    #[test]
412    fn flip() {
413        assert_eq!(Cmp::Eq.flip(), Cmp::Eq);
414        assert_eq!(Cmp::Ne.flip(), Cmp::Ne);
415        assert_eq!(Cmp::Lt.flip(), Cmp::Gt);
416        assert_eq!(Cmp::Le.flip(), Cmp::Ge);
417        assert_eq!(Cmp::Ge.flip(), Cmp::Le);
418        assert_eq!(Cmp::Gt.flip(), Cmp::Lt);
419    }
420
421    #[test]
422    fn sign() {
423        assert_eq!(Cmp::Eq.sign(), "==");
424        assert_eq!(Cmp::Ne.sign(), "!=");
425        assert_eq!(Cmp::Lt.sign(), "<");
426        assert_eq!(Cmp::Le.sign(), "<=");
427        assert_eq!(Cmp::Ge.sign(), ">=");
428        assert_eq!(Cmp::Gt.sign(), ">");
429    }
430
431    #[test]
432    fn factor() {
433        assert_eq!(Cmp::Eq.factor(), 0);
434        assert_eq!(Cmp::Ne.factor(), 0);
435        assert_eq!(Cmp::Lt.factor(), -1);
436        assert_eq!(Cmp::Le.factor(), -1);
437        assert_eq!(Cmp::Ge.factor(), 1);
438        assert_eq!(Cmp::Gt.factor(), 1);
439    }
440
441    #[test]
442    fn ord() {
443        assert_eq!(Cmp::Eq.ord(), Some(Ordering::Equal));
444        assert_eq!(Cmp::Ne.ord(), None);
445        assert_eq!(Cmp::Lt.ord(), Some(Ordering::Less));
446        assert_eq!(Cmp::Le.ord(), None);
447        assert_eq!(Cmp::Ge.ord(), None);
448        assert_eq!(Cmp::Gt.ord(), Some(Ordering::Greater));
449    }
450}