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 | |
9 | use std::cmp::Ordering; |
10 | |
11 | /// Comparison operators enum. |
12 | #[derive (Debug, Copy, Clone, PartialEq, Eq, Hash)] |
13 | pub 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 | |
39 | impl 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 | |
317 | impl 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)] |
336 | mod 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 | |