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}
451