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 `CompOp::from_sign(">");` can be used to get a comparison operator by it's logical
7 //! sign from a string.
8 
9 use std::cmp::Ordering;
10 
11 /// Enum of supported comparison operators.
12 #[derive(Debug, Clone, PartialEq)]
13 pub enum CompOp {
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 
39 impl CompOp {
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::CompOp;
57     ///
58     /// assert_eq!(CompOp::from_sign("=="), Ok(CompOp::Eq));
59     /// assert_eq!(CompOp::from_sign("<"), Ok(CompOp::Lt));
60     /// assert_eq!(CompOp::from_sign("  >=   "), Ok(CompOp::Ge));
61     /// assert!(CompOp::from_sign("*").is_err());
62     /// ```
from_sign(sign: &str) -> Result<CompOp, ()>63     pub fn from_sign(sign: &str) -> Result<CompOp, ()> {
64         match sign.trim().as_ref() {
65             "==" | "=" => Ok(CompOp::Eq),
66             "!=" | "!" | "<>" => Ok(CompOp::Ne),
67             "<" => Ok(CompOp::Lt),
68             "<=" => Ok(CompOp::Le),
69             ">=" => Ok(CompOp::Ge),
70             ">" => Ok(CompOp::Gt),
71             _ => Err(()),
72         }
73     }
74 
75     /// Get a comparison operator by it's name.
76     /// Names are case-insensitive, and whitespaces are stripped from the string.
77     /// An error is returned if the name isn't recognized.
78     ///
79     /// # Examples
80     ///
81     /// ```
82     /// use version_compare::CompOp;
83     ///
84     /// assert_eq!(CompOp::from_name("eq"), Ok(CompOp::Eq));
85     /// assert_eq!(CompOp::from_name("lt"), Ok(CompOp::Lt));
86     /// assert_eq!(CompOp::from_name("  Ge   "), Ok(CompOp::Ge));
87     /// assert!(CompOp::from_name("abc").is_err());
88     /// ```
from_name(sign: &str) -> Result<CompOp, ()>89     pub fn from_name(sign: &str) -> Result<CompOp, ()> {
90         match sign.trim().to_lowercase().as_ref() {
91             "eq" => Ok(CompOp::Eq),
92             "ne" => Ok(CompOp::Ne),
93             "lt" => Ok(CompOp::Lt),
94             "le" => Ok(CompOp::Le),
95             "ge" => Ok(CompOp::Ge),
96             "gt" => Ok(CompOp::Gt),
97             _ => Err(()),
98         }
99     }
100 
101     /// Get the comparison operator from Rusts `Ordering` enum.
102     ///
103     /// The following comparison operators are returned:
104     ///
105     /// * `Ordering::Less` -> `Lt`
106     /// * `Ordering::Equal` -> `Eq`
107     /// * `Ordering::Greater` -> `Gt`
from_ord(ord: Ordering) -> CompOp108     pub fn from_ord(ord: Ordering) -> CompOp {
109         match ord {
110             Ordering::Less => CompOp::Lt,
111             Ordering::Equal => CompOp::Eq,
112             Ordering::Greater => CompOp::Gt,
113         }
114     }
115 
116     /// Get the name of this comparison operator.
117     ///
118     /// # Examples
119     ///
120     /// ```
121     /// use version_compare::CompOp;
122     ///
123     /// assert_eq!(CompOp::Eq.name(), "eq");
124     /// assert_eq!(CompOp::Lt.name(), "lt");
125     /// assert_eq!(CompOp::Ge.name(), "ge");
126     /// ```
name(&self) -> &str127     pub fn name(&self) -> &str {
128         match self {
129             &CompOp::Eq => "eq",
130             &CompOp::Ne => "ne",
131             &CompOp::Lt => "lt",
132             &CompOp::Le => "le",
133             &CompOp::Ge => "ge",
134             &CompOp::Gt => "gt",
135         }
136     }
137 
138     /// Covert to the inverted comparison operator.
139     ///
140     /// This uses the following bidirectional rules:
141     ///
142     /// * `Eq` <-> `Ne`
143     /// * `Lt` <-> `Ge`
144     /// * `Le` <-> `Gt`
145     ///
146     /// # Examples
147     ///
148     /// ```
149     /// use version_compare::CompOp;
150     ///
151     /// assert_eq!(CompOp::Eq.as_inverted(), CompOp::Ne);
152     /// assert_eq!(CompOp::Lt.as_inverted(), CompOp::Ge);
153     /// assert_eq!(CompOp::Gt.as_inverted(), CompOp::Le);
154     /// ```
as_inverted(self) -> Self155     pub fn as_inverted(self) -> Self {
156         self.invert()
157     }
158 
159     /// Get the inverted comparison operator.
160     ///
161     /// This uses the following bidirectional rules:
162     ///
163     /// * `Eq` <-> `Ne`
164     /// * `Lt` <-> `Ge`
165     /// * `Le` <-> `Gt`
166     ///
167     /// # Examples
168     ///
169     /// ```
170     /// use version_compare::CompOp;
171     ///
172     /// assert_eq!(CompOp::Eq.invert(), CompOp::Ne);
173     /// assert_eq!(CompOp::Lt.invert(), CompOp::Ge);
174     /// assert_eq!(CompOp::Gt.invert(), CompOp::Le);
175     /// ```
invert(&self) -> Self176     pub fn invert(&self) -> Self {
177         match self {
178             &CompOp::Eq => CompOp::Ne,
179             &CompOp::Ne => CompOp::Eq,
180             &CompOp::Lt => CompOp::Ge,
181             &CompOp::Le => CompOp::Gt,
182             &CompOp::Ge => CompOp::Lt,
183             &CompOp::Gt => CompOp::Le,
184         }
185     }
186 
187     /// Convert to the opposite comparison operator.
188     ///
189     /// This uses the following bidirectional rules:
190     ///
191     /// * `Eq` <-> `Ne`
192     /// * `Lt` <-> `Gt`
193     /// * `Le` <-> `Ge`
194     ///
195     /// # Examples
196     ///
197     /// ```
198     /// use version_compare::CompOp;
199     ///
200     /// assert_eq!(CompOp::Eq.as_opposite(), CompOp::Ne);
201     /// assert_eq!(CompOp::Lt.as_opposite(), CompOp::Gt);
202     /// assert_eq!(CompOp::Ge.as_opposite(), CompOp::Le);
203     /// ```
as_opposite(self) -> Self204     pub fn as_opposite(self) -> Self {
205         self.opposite()
206     }
207 
208     /// Get the opposite comparison operator.
209     ///
210     /// This uses the following bidirectional rules:
211     ///
212     /// * `Eq` <-> `Ne`
213     /// * `Lt` <-> `Gt`
214     /// * `Le` <-> `Ge`
215     ///
216     /// # Examples
217     ///
218     /// ```
219     /// use version_compare::CompOp;
220     ///
221     /// assert_eq!(CompOp::Eq.opposite(), CompOp::Ne);
222     /// assert_eq!(CompOp::Lt.opposite(), CompOp::Gt);
223     /// assert_eq!(CompOp::Ge.opposite(), CompOp::Le);
224     /// ```
opposite(&self) -> Self225     pub fn opposite(&self) -> Self {
226         match self {
227             &CompOp::Eq => CompOp::Ne,
228             &CompOp::Ne => CompOp::Eq,
229             &CompOp::Lt => CompOp::Gt,
230             &CompOp::Le => CompOp::Ge,
231             &CompOp::Ge => CompOp::Le,
232             &CompOp::Gt => CompOp::Lt,
233         }
234     }
235 
236     /// Convert to the flipped comparison operator.
237     ///
238     /// This uses the following bidirectional rules:
239     ///
240     /// * `Lt` <-> `Gt`
241     /// * `Le` <-> `Ge`
242     /// * Other operators are returned as is.
243     ///
244     /// # Examples
245     ///
246     /// ```
247     /// use version_compare::CompOp;
248     ///
249     /// assert_eq!(CompOp::Eq.as_flipped(), CompOp::Eq);
250     /// assert_eq!(CompOp::Lt.as_flipped(), CompOp::Gt);
251     /// assert_eq!(CompOp::Ge.as_flipped(), CompOp::Le);
252     /// ```
as_flipped(self) -> Self253     pub fn as_flipped(self) -> Self {
254         self.flip()
255     }
256 
257     /// Get the flipped comparison operator.
258     ///
259     /// This uses the following bidirectional rules:
260     ///
261     /// * `Lt` <-> `Gt`
262     /// * `Le` <-> `Ge`
263     /// * Other operators are returned as is.
264     ///
265     /// # Examples
266     ///
267     /// ```
268     /// use version_compare::CompOp;
269     ///
270     /// assert_eq!(CompOp::Eq.flip(), CompOp::Eq);
271     /// assert_eq!(CompOp::Lt.flip(), CompOp::Gt);
272     /// assert_eq!(CompOp::Ge.flip(), CompOp::Le);
273     /// ```
flip(&self) -> Self274     pub fn flip(&self) -> Self {
275         match self {
276             &CompOp::Lt => CompOp::Gt,
277             &CompOp::Le => CompOp::Ge,
278             &CompOp::Ge => CompOp::Le,
279             &CompOp::Gt => CompOp::Lt,
280             _ => self.clone(),
281         }
282     }
283 
284     /// Get the sign for this comparison operator.
285     ///
286     /// The following signs are returned:
287     ///
288     /// * `Eq` -> `==`
289     /// * `Ne` -> `!=`
290     /// * `Lt` -> `< `
291     /// * `Le` -> `<=`
292     /// * `Ge` -> `>=`
293     /// * `Gt` -> `> `
294     ///
295     /// Note: Some comparison operators also support other signs,
296     /// such as `=` for `Eq` and `!` for `Ne`,
297     /// these are never returned by this method however as the table above is used.
298     ///
299     /// # Examples
300     ///
301     /// ```
302     /// use version_compare::CompOp;
303     ///
304     /// assert_eq!(CompOp::Eq.sign(), "==");
305     /// assert_eq!(CompOp::Lt.sign(), "<");
306     /// assert_eq!(CompOp::Ge.flip().sign(), "<=");
307     /// ```
sign(&self) -> &'static str308     pub fn sign(&self) -> &'static str {
309         match self {
310             &CompOp::Eq => "==",
311             &CompOp::Ne => "!=",
312             &CompOp::Lt => "<",
313             &CompOp::Le => "<=",
314             &CompOp::Ge => ">=",
315             &CompOp::Gt => ">",
316         }
317     }
318 
319     /// Get a factor (number) for this comparison operator.
320     /// These factors can be useful for quick calculations.
321     ///
322     /// The following factor numbers are returned:
323     ///
324     /// * `Eq` or `Ne` -> ` 0 `
325     /// * `Lt` or `Le` -> `-1`
326     /// * `Gt` or `Ge` -> ` 1`
327     ///
328     /// # Examples
329     ///
330     /// ```
331     /// use version_compare::Version;
332     ///
333     /// let ver_a = Version::from("1.2.3").unwrap();
334     /// let ver_b = Version::from("1.3").unwrap();
335     ///
336     /// assert_eq!(ver_a.compare(&ver_b).factor(), -1);
337     /// assert_eq!(10 * ver_b.compare(&ver_a).factor(), 10);
338     /// ```
factor(&self) -> i8339     pub fn factor(&self) -> i8 {
340         match self {
341             &CompOp::Eq | &CompOp::Ne => 0,
342             &CompOp::Lt | &CompOp::Le => -1,
343             &CompOp::Gt | &CompOp::Ge => 1,
344         }
345     }
346 
347     /// Get Rust's ordering for this comparison operator.
348     ///
349     /// The following comparison operators are supported:
350     ///
351     /// * `Eq` -> `Ordering::Equal`
352     /// * `Lt` -> `Ordering::Less`
353     /// * `Gt` -> `Ordering::Greater`
354     ///
355     /// For other comparison operators `None` is returned.
356     ///
357     /// # Examples
358     ///
359     /// ```
360     /// use std::cmp::Ordering;
361     /// use version_compare::Version;
362     ///
363     /// let ver_a = Version::from("1.2.3").unwrap();
364     /// let ver_b = Version::from("1.3").unwrap();
365     ///
366     /// assert_eq!(ver_a.compare(&ver_b).ord().unwrap(), Ordering::Less);
367     /// ```
ord(&self) -> Option<Ordering>368     pub fn ord(&self) -> Option<Ordering> {
369         match self {
370             &CompOp::Eq => Some(Ordering::Equal),
371             &CompOp::Lt => Some(Ordering::Less),
372             &CompOp::Gt => Some(Ordering::Greater),
373             _ => None,
374         }
375     }
376 }
377 
378 #[cfg_attr(tarpaulin, skip)]
379 #[cfg(test)]
380 mod tests {
381     use std::cmp::Ordering;
382 
383     use super::CompOp;
384 
385     #[test]
from_sign()386     fn from_sign() {
387         // Normal signs
388         assert_eq!(CompOp::from_sign("==").unwrap(), CompOp::Eq);
389         assert_eq!(CompOp::from_sign("=").unwrap(), CompOp::Eq);
390         assert_eq!(CompOp::from_sign("!=").unwrap(), CompOp::Ne);
391         assert_eq!(CompOp::from_sign("!").unwrap(), CompOp::Ne);
392         assert_eq!(CompOp::from_sign("<>").unwrap(), CompOp::Ne);
393         assert_eq!(CompOp::from_sign("<").unwrap(), CompOp::Lt);
394         assert_eq!(CompOp::from_sign("<=").unwrap(), CompOp::Le);
395         assert_eq!(CompOp::from_sign(">=").unwrap(), CompOp::Ge);
396         assert_eq!(CompOp::from_sign(">").unwrap(), CompOp::Gt);
397 
398         // Exceptional cases
399         assert_eq!(CompOp::from_sign("  <=  ").unwrap(), CompOp::Le);
400         assert!(CompOp::from_sign("*").is_err());
401     }
402 
403     #[test]
from_name()404     fn from_name() {
405         // Normal names
406         assert_eq!(CompOp::from_name("eq").unwrap(), CompOp::Eq);
407         assert_eq!(CompOp::from_name("ne").unwrap(), CompOp::Ne);
408         assert_eq!(CompOp::from_name("lt").unwrap(), CompOp::Lt);
409         assert_eq!(CompOp::from_name("le").unwrap(), CompOp::Le);
410         assert_eq!(CompOp::from_name("ge").unwrap(), CompOp::Ge);
411         assert_eq!(CompOp::from_name("gt").unwrap(), CompOp::Gt);
412 
413         // Exceptional cases
414         assert_eq!(CompOp::from_name("  Le  ").unwrap(), CompOp::Le);
415         assert!(CompOp::from_name("abc").is_err());
416     }
417 
418     #[test]
from_ord()419     fn from_ord() {
420         assert_eq!(CompOp::from_ord(Ordering::Less), CompOp::Lt);
421         assert_eq!(CompOp::from_ord(Ordering::Equal), CompOp::Eq);
422         assert_eq!(CompOp::from_ord(Ordering::Greater), CompOp::Gt);
423     }
424 
425     #[test]
name()426     fn name() {
427         assert_eq!(CompOp::Eq.name(), "eq");
428         assert_eq!(CompOp::Ne.name(), "ne");
429         assert_eq!(CompOp::Lt.name(), "lt");
430         assert_eq!(CompOp::Le.name(), "le");
431         assert_eq!(CompOp::Ge.name(), "ge");
432         assert_eq!(CompOp::Gt.name(), "gt");
433     }
434 
435     #[test]
as_inverted()436     fn as_inverted() {
437         assert_eq!(CompOp::Ne.as_inverted(), CompOp::Eq);
438         assert_eq!(CompOp::Eq.as_inverted(), CompOp::Ne);
439         assert_eq!(CompOp::Ge.as_inverted(), CompOp::Lt);
440         assert_eq!(CompOp::Gt.as_inverted(), CompOp::Le);
441         assert_eq!(CompOp::Lt.as_inverted(), CompOp::Ge);
442         assert_eq!(CompOp::Le.as_inverted(), CompOp::Gt);
443     }
444 
445     #[test]
invert()446     fn invert() {
447         assert_eq!(CompOp::Ne.invert(), CompOp::Eq);
448         assert_eq!(CompOp::Eq.invert(), CompOp::Ne);
449         assert_eq!(CompOp::Ge.invert(), CompOp::Lt);
450         assert_eq!(CompOp::Gt.invert(), CompOp::Le);
451         assert_eq!(CompOp::Lt.invert(), CompOp::Ge);
452         assert_eq!(CompOp::Le.invert(), CompOp::Gt);
453     }
454 
455     #[test]
as_opposite()456     fn as_opposite() {
457         assert_eq!(CompOp::Ne.as_opposite(), CompOp::Eq);
458         assert_eq!(CompOp::Eq.as_opposite(), CompOp::Ne);
459         assert_eq!(CompOp::Gt.as_opposite(), CompOp::Lt);
460         assert_eq!(CompOp::Ge.as_opposite(), CompOp::Le);
461         assert_eq!(CompOp::Le.as_opposite(), CompOp::Ge);
462         assert_eq!(CompOp::Lt.as_opposite(), CompOp::Gt);
463     }
464 
465     #[test]
opposite()466     fn opposite() {
467         assert_eq!(CompOp::Eq.opposite(), CompOp::Ne);
468         assert_eq!(CompOp::Ne.opposite(), CompOp::Eq);
469         assert_eq!(CompOp::Lt.opposite(), CompOp::Gt);
470         assert_eq!(CompOp::Le.opposite(), CompOp::Ge);
471         assert_eq!(CompOp::Ge.opposite(), CompOp::Le);
472         assert_eq!(CompOp::Gt.opposite(), CompOp::Lt);
473     }
474 
475     #[test]
as_flipped()476     fn as_flipped() {
477         assert_eq!(CompOp::Eq.as_flipped(), CompOp::Eq);
478         assert_eq!(CompOp::Ne.as_flipped(), CompOp::Ne);
479         assert_eq!(CompOp::Lt.as_flipped(), CompOp::Gt);
480         assert_eq!(CompOp::Le.as_flipped(), CompOp::Ge);
481         assert_eq!(CompOp::Ge.as_flipped(), CompOp::Le);
482         assert_eq!(CompOp::Gt.as_flipped(), CompOp::Lt);
483     }
484 
485     #[test]
flip()486     fn flip() {
487         assert_eq!(CompOp::Eq.flip(), CompOp::Eq);
488         assert_eq!(CompOp::Ne.flip(), CompOp::Ne);
489         assert_eq!(CompOp::Lt.flip(), CompOp::Gt);
490         assert_eq!(CompOp::Le.flip(), CompOp::Ge);
491         assert_eq!(CompOp::Ge.flip(), CompOp::Le);
492         assert_eq!(CompOp::Gt.flip(), CompOp::Lt);
493     }
494 
495     #[test]
sign()496     fn sign() {
497         assert_eq!(CompOp::Eq.sign(), "==");
498         assert_eq!(CompOp::Ne.sign(), "!=");
499         assert_eq!(CompOp::Lt.sign(), "<");
500         assert_eq!(CompOp::Le.sign(), "<=");
501         assert_eq!(CompOp::Ge.sign(), ">=");
502         assert_eq!(CompOp::Gt.sign(), ">");
503     }
504 
505     #[test]
factor()506     fn factor() {
507         assert_eq!(CompOp::Eq.factor(), 0);
508         assert_eq!(CompOp::Ne.factor(), 0);
509         assert_eq!(CompOp::Lt.factor(), -1);
510         assert_eq!(CompOp::Le.factor(), -1);
511         assert_eq!(CompOp::Ge.factor(), 1);
512         assert_eq!(CompOp::Gt.factor(), 1);
513     }
514 
515     #[test]
ord()516     fn ord() {
517         assert_eq!(CompOp::Eq.ord(), Some(Ordering::Equal));
518         assert_eq!(CompOp::Ne.ord(), None);
519         assert_eq!(CompOp::Lt.ord(), Some(Ordering::Less));
520         assert_eq!(CompOp::Le.ord(), None);
521         assert_eq!(CompOp::Ge.ord(), None);
522         assert_eq!(CompOp::Gt.ord(), Some(Ordering::Greater));
523     }
524 }
525