1 //! Version module, which provides the `Version` struct as parsed version representation.
2 //!
3 //! Version numbers in the form of a string are parsed to a `Version` first, before any comparison
4 //! is made. This struct provides many methods and features for easy comparison, probing and other
5 //! things.
6 
7 use std::cmp::Ordering;
8 use std::fmt;
9 use std::iter::Peekable;
10 use std::slice::Iter;
11 
12 use crate::comp_op::CompOp;
13 use crate::version_manifest::VersionManifest;
14 use crate::version_part::VersionPart;
15 
16 /// Version struct, which is a representation for a parsed version string.
17 ///
18 /// A version in string format can be parsed using methods like `Version::from("1.2.3");`.
19 /// These methods return a `Result` holding the parsed version or an error on failure.
20 ///
21 /// The original version string is stored in the struct, and can be accessed using the
22 /// `version.as_str()` method. Note, that when the version wasn't parsed from a string
23 /// representation, the returned value is generated.
24 ///
25 /// The struct provides many methods for comparison and probing.
26 pub struct Version<'a> {
27     version: &'a str,
28     parts: Vec<VersionPart<'a>>,
29     manifest: Option<&'a VersionManifest>,
30 }
31 
32 impl<'a> Version<'a> {
33     /// Create a `Version` instance from a version string.
34     ///
35     /// The version string should be passed to the `version` parameter.
36     ///
37     /// # Examples
38     ///
39     /// ```
40     /// use version_compare::{CompOp, Version};
41     ///
42     /// let ver = Version::from("1.2.3").unwrap();
43     ///
44     /// assert_eq!(ver.compare(&Version::from("1.2.3").unwrap()), CompOp::Eq);
45     /// ```
from(version: &'a str) -> Option<Self>46     pub fn from(version: &'a str) -> Option<Self> {
47         // Split the version string
48         let parts = Self::split_version_str(version, None);
49 
50         // Return nothing if the parts are none
51         if parts.is_none() {
52             return None;
53         }
54 
55         // Create and return the object
56         Some(Version {
57             version: version,
58             parts: parts.unwrap(),
59             manifest: None,
60         })
61     }
62 
63     /// Create a `Version` instance from already existing parts
64     ///
65     ///
66     /// # Examples
67     ///
68     /// ```
69     /// use version_compare::{CompOp, Version, VersionManifest};
70     /// ```
from_parts(version: &'a str, version_parts: Vec<VersionPart<'a>>) -> Self71     pub fn from_parts(version: &'a str, version_parts: Vec<VersionPart<'a>>) -> Self {
72         Version {
73             version: version,
74             parts: version_parts,
75             manifest: None,
76         }
77     }
78 
79     /// Create a `Version` instance from a version string with the given `manifest`.
80     ///
81     /// The version string should be passed to the `version` parameter.
82     ///
83     /// # Examples
84     ///
85     /// ```
86     /// use version_compare::{CompOp, Version, VersionManifest};
87     ///
88     /// let manifest = VersionManifest::new();
89     /// let ver = Version::from_manifest("1.2.3", &manifest).unwrap();
90     ///
91     /// assert_eq!(ver.compare(&Version::from("1.2.3").unwrap()), CompOp::Eq);
92     /// ```
from_manifest(version: &'a str, manifest: &'a VersionManifest) -> Option<Self>93     pub fn from_manifest(version: &'a str, manifest: &'a VersionManifest) -> Option<Self> {
94         // Split the version string
95         let parts = Self::split_version_str(version, Some(&manifest));
96 
97         // Return nothing if the parts are none
98         if parts.is_none() {
99             return None;
100         }
101 
102         // Create and return the object
103         Some(Version {
104             version: version,
105             parts: parts.unwrap(),
106             manifest: Some(&manifest),
107         })
108     }
109 
110     /// Get the version manifest, if available.
111     ///
112     /// # Examples
113     ///
114     /// ```
115     /// use version_compare::Version;
116     ///
117     /// let version = Version::from("1.2.3").unwrap();
118     ///
119     /// if version.has_manifest() {
120     ///     println!(
121     ///         "Maximum version part depth is {} for this version",
122     ///         version.manifest().unwrap().max_depth_number()
123     ///     );
124     /// } else {
125     ///     println!("Version has no manifest");
126     /// }
127     /// ```
manifest(&self) -> Option<&VersionManifest>128     pub fn manifest(&self) -> Option<&VersionManifest> {
129         self.manifest
130     }
131 
132     /// Check whether this version has a manifest.
133     ///
134     /// # Examples
135     ///
136     /// ```
137     /// use version_compare::Version;
138     ///
139     /// let version = Version::from("1.2.3").unwrap();
140     ///
141     /// if version.has_manifest() {
142     ///     println!("This version does have a manifest");
143     /// } else {
144     ///     println!("This version does not have a manifest");
145     /// }
146     /// ```
has_manifest(&self) -> bool147     pub fn has_manifest(&self) -> bool {
148         self.manifest().is_some()
149     }
150 
151     /// Set the version manifest.
152     ///
153     /// # Examples
154     ///
155     /// ```
156     /// use version_compare::{Version, VersionManifest};
157     ///
158     /// let manifest = VersionManifest::new();
159     /// let mut version = Version::from("1.2.3").unwrap();
160     ///
161     /// version.set_manifest(Some(&manifest));
162     /// ```
set_manifest(&mut self, manifest: Option<&'a VersionManifest>)163     pub fn set_manifest(&mut self, manifest: Option<&'a VersionManifest>) {
164         self.manifest = manifest;
165 
166         // TODO: Re-parse the version string, because the manifest might have changed.
167     }
168 
169     /// Split the given version string, in it's version parts.
170     /// TODO: Move this method to some sort of helper class, maybe as part of `VersionPart`.
split_version_str( version: &'a str, manifest: Option<&'a VersionManifest>, ) -> Option<Vec<VersionPart<'a>>>171     fn split_version_str(
172         version: &'a str,
173         manifest: Option<&'a VersionManifest>,
174     ) -> Option<Vec<VersionPart<'a>>> {
175         // Split the version string, and create a vector to put the parts in
176         // TODO: split at specific separators instead
177         let split = version.split(|c| !char::is_alphanumeric(c));
178         let mut parts = Vec::new();
179 
180         // Get the manifest to follow
181         let mut used_manifest = &VersionManifest::new();
182         if manifest.is_some() {
183             used_manifest = manifest.unwrap();
184         }
185 
186         // Flag to determine whether this version number contains any number part
187         let mut has_number = false;
188 
189         // Loop over the parts, and parse them
190         for part in split {
191             // We may not go over the maximum depth
192             if used_manifest.max_depth().is_some()
193                 && parts.len() >= used_manifest.max_depth_number()
194             {
195                 break;
196             }
197 
198             // Skip empty parts
199             if part.is_empty() {
200                 continue;
201             }
202 
203             // Try to parse the value as an number
204             match part.parse::<i32>() {
205                 Ok(number) => {
206                     // Push the number part to the vector, and set the has number flag
207                     parts.push(VersionPart::Number(number));
208                     has_number = true;
209                 }
210                 Err(_) => {
211                     // Ignore text parts if specified
212                     if used_manifest.ignore_text() {
213                         continue;
214                     }
215 
216                     // Push the text part to the vector
217                     parts.push(VersionPart::Text(part))
218                 }
219             }
220         }
221 
222         // The version must contain a number part, if any part was parsed
223         if !has_number && !parts.is_empty() {
224             return None;
225         }
226 
227         // Return the list of parts
228         Some(parts)
229     }
230 
231     /// Get the original version string.
232     ///
233     /// # Examples
234     ///
235     /// ```
236     /// use version_compare::Version;
237     ///
238     /// let ver = Version::from("1.2.3").unwrap();
239     ///
240     /// assert_eq!(ver.as_str(), "1.2.3");
241     /// ```
as_str(&self) -> &str242     pub fn as_str(&self) -> &str {
243         &self.version
244     }
245 
246     /// Get a specific version part by it's `index`.
247     /// An error is returned if the given index is out of bound.
248     ///
249     /// # Examples
250     ///
251     /// ```
252     /// use version_compare::{Version, VersionPart};
253     ///
254     /// let ver = Version::from("1.2.3").unwrap();
255     ///
256     /// assert_eq!(ver.part(0), Ok(&VersionPart::Number(1)));
257     /// assert_eq!(ver.part(1), Ok(&VersionPart::Number(2)));
258     /// assert_eq!(ver.part(2), Ok(&VersionPart::Number(3)));
259     /// ```
part(&self, index: usize) -> Result<&VersionPart<'a>, ()>260     pub fn part(&self, index: usize) -> Result<&VersionPart<'a>, ()> {
261         // Make sure the index is in-bound
262         if index >= self.parts.len() {
263             return Err(());
264         }
265 
266         // Return the requested part
267         Ok(&self.parts[index])
268     }
269 
270     /// Get a vector of all version parts.
271     ///
272     /// # Examples
273     ///
274     /// ```
275     /// use version_compare::{Version, VersionPart};
276     ///
277     /// let ver = Version::from("1.2.3").unwrap();
278     ///
279     /// assert_eq!(ver.parts(), &vec![
280     ///     VersionPart::Number(1),
281     ///     VersionPart::Number(2),
282     ///     VersionPart::Number(3)
283     /// ]);
284     /// ```
parts(&self) -> &Vec<VersionPart<'a>>285     pub fn parts(&self) -> &Vec<VersionPart<'a>> {
286         &self.parts
287     }
288 
289     /// Get the number of parts in this version string.
290     ///
291     /// # Examples
292     ///
293     /// ```
294     /// use version_compare::Version;
295     ///
296     /// let ver_a = Version::from("1.2.3").unwrap();
297     /// let ver_b = Version::from("1.2.3.4").unwrap();
298     ///
299     /// assert_eq!(ver_a.part_count(), 3);
300     /// assert_eq!(ver_b.part_count(), 4);
301     /// ```
part_count(&self) -> usize302     pub fn part_count(&self) -> usize {
303         self.parts.len()
304     }
305 
306     /// Compare this version to the given `other` version.
307     ///
308     /// This method returns one of the following comparison operators:
309     ///
310     /// * `Lt`
311     /// * `Eq`
312     /// * `Gt`
313     ///
314     /// Other comparison operators can be used when comparing, but aren't returned by this method.
315     ///
316     /// # Examples:
317     ///
318     /// ```
319     /// use version_compare::{CompOp, Version};
320     ///
321     /// assert_eq!(Version::from("1.2").unwrap().compare(&Version::from("1.3.2").unwrap()), CompOp::Lt);
322     /// assert_eq!(Version::from("1.9").unwrap().compare(&Version::from("1.9").unwrap()), CompOp::Eq);
323     /// assert_eq!(Version::from("0.3.0.0").unwrap().compare(&Version::from("0.3").unwrap()), CompOp::Eq);
324     /// assert_eq!(Version::from("2").unwrap().compare(&Version::from("1.7.3").unwrap()), CompOp::Gt);
325     /// ```
compare(&self, other: &'a Version) -> CompOp326     pub fn compare(&self, other: &'a Version) -> CompOp {
327         // Compare the versions with their peekable iterators
328         Self::compare_iter(self.parts.iter().peekable(), other.parts.iter().peekable())
329     }
330 
331     /// Compare this version to the given `other` version,
332     /// and check whether the given comparison operator is valid.
333     ///
334     /// All comparison operators can be used.
335     ///
336     /// # Examples:
337     ///
338     /// ```
339     /// use version_compare::{CompOp, Version};
340     ///
341     /// assert!(Version::from("1.2").unwrap().compare_to(&Version::from("1.3.2").unwrap(), &CompOp::Lt));
342     /// assert!(Version::from("1.2").unwrap().compare_to(&Version::from("1.3.2").unwrap(), &CompOp::Le));
343     /// assert!(Version::from("1.2").unwrap().compare_to(&Version::from("1.2").unwrap(), &CompOp::Eq));
344     /// assert!(Version::from("1.2").unwrap().compare_to(&Version::from("1.2").unwrap(), &CompOp::Le));
345     /// ```
compare_to(&self, other: &Version, operator: &CompOp) -> bool346     pub fn compare_to(&self, other: &Version, operator: &CompOp) -> bool {
347         // Get the comparison result
348         let result = self.compare(&other);
349 
350         // Match the result against the given operator
351         match result {
352             CompOp::Eq => match operator {
353                 &CompOp::Eq | &CompOp::Le | &CompOp::Ge => true,
354                 _ => false,
355             },
356             CompOp::Lt => match operator {
357                 &CompOp::Ne | &CompOp::Lt | &CompOp::Le => true,
358                 _ => false,
359             },
360             CompOp::Gt => match operator {
361                 &CompOp::Ne | &CompOp::Gt | &CompOp::Ge => true,
362                 _ => false,
363             },
364             _ => unreachable!(),
365         }
366     }
367 
368     /// Compare two version numbers based on the iterators of their version parts.
369     ///
370     /// This method returns one of the following comparison operators:
371     ///
372     /// * `Lt`
373     /// * `Eq`
374     /// * `Gt`
375     ///
376     /// Other comparison operators can be used when comparing, but aren't returned by this method.
compare_iter( mut iter: Peekable<Iter<VersionPart<'a>>>, mut other_iter: Peekable<Iter<VersionPart<'a>>>, ) -> CompOp377     fn compare_iter(
378         mut iter: Peekable<Iter<VersionPart<'a>>>,
379         mut other_iter: Peekable<Iter<VersionPart<'a>>>,
380     ) -> CompOp {
381         // Iterate through the parts of this version
382         let mut other_part: Option<&VersionPart>;
383 
384         // Iterate over the iterator, without consuming it
385         loop {
386             match iter.next() {
387                 Some(part) => {
388                     // Get the part for the other version
389                     other_part = other_iter.next();
390 
391                     // If there are no parts left in the other version, try to determine the result
392                     if other_part.is_none() {
393                         // In the main version: if the current part is zero, continue to the next one
394                         match part {
395                             &VersionPart::Number(num) => {
396                                 if num == 0 {
397                                     continue;
398                                 }
399                             }
400                             &VersionPart::Text(_) => return CompOp::Lt,
401                         }
402 
403                         // The main version is greater
404                         return CompOp::Gt;
405                     }
406 
407                     // Match both part as numbers to destruct their numerical values
408                     match part {
409                         &VersionPart::Number(num) => match other_part.unwrap() {
410                             &VersionPart::Number(other_num) => {
411                                 // Compare the numbers
412                                 match num {
413                                     n if n < other_num => return CompOp::Lt,
414                                     n if n > other_num => return CompOp::Gt,
415                                     _ => continue,
416                                 }
417                             }
418                             _ => {}
419                         },
420                         _ => {}
421                     }
422                 }
423                 None => break,
424             }
425         }
426 
427         // Check whether we should iterate over the other iterator, if it has any items left
428         match other_iter.peek() {
429             // Compare based on the other iterator
430             Some(_) => Self::compare_iter(other_iter, iter).as_flipped(),
431 
432             // Nothing more to iterate over, the versions should be equal
433             None => CompOp::Eq,
434         }
435     }
436 }
437 
438 impl<'a> fmt::Display for Version<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result439     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
440         write!(f, "{}", self.version)
441     }
442 }
443 
444 // Show just the version component parts as debug output
445 impl<'a> fmt::Debug for Version<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result446     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
447         if f.alternate() {
448             write!(f, "{:#?}", self.parts)
449         } else {
450             write!(f, "{:?}", self.parts)
451         }
452     }
453 }
454 
455 /// Implement the partial ordering trait for the version struct, to easily allow version comparison.
456 impl<'a> PartialOrd for Version<'a> {
partial_cmp(&self, other: &Self) -> Option<Ordering>457     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
458         Some(self.compare(other).ord().unwrap())
459     }
460 }
461 
462 /// Implement the partial equality trait for the version struct, to easily allow version comparison.
463 impl<'a> PartialEq for Version<'a> {
eq(&self, other: &Self) -> bool464     fn eq(&self, other: &Self) -> bool {
465         self.compare_to(other, &CompOp::Eq)
466     }
467 }
468 
469 #[cfg_attr(tarpaulin, skip)]
470 #[cfg(test)]
471 mod tests {
472     use std::cmp;
473 
474     use crate::comp_op::CompOp;
475     use crate::test::test_version::{TEST_VERSIONS, TEST_VERSIONS_ERROR};
476     use crate::test::test_version_set::TEST_VERSION_SETS;
477     use crate::version_manifest::VersionManifest;
478     use crate::version_part::VersionPart;
479 
480     use super::Version;
481 
482     #[test]
483     // TODO: This doesn't really test whether this method fully works
from()484     fn from() {
485         // Test whether parsing works for each test version
486         for version in TEST_VERSIONS {
487             assert!(Version::from(&version.0).is_some());
488         }
489 
490         // Test whether parsing works for each test invalid version
491         for version in TEST_VERSIONS_ERROR {
492             assert!(Version::from(&version.0).is_none());
493         }
494     }
495 
496     #[test]
497     // TODO: This doesn't really test whether this method fully works
from_manifest()498     fn from_manifest() {
499         // Create a manifest
500         let manifest = VersionManifest::new();
501 
502         // Test whether parsing works for each test version
503         for version in TEST_VERSIONS {
504             assert_eq!(
505                 Version::from_manifest(&version.0, &manifest)
506                     .unwrap()
507                     .manifest,
508                 Some(&manifest)
509             );
510         }
511 
512         // Test whether parsing works for each test invalid version
513         for version in TEST_VERSIONS_ERROR {
514             assert!(Version::from_manifest(&version.0, &manifest).is_none());
515         }
516     }
517 
518     #[test]
manifest()519     fn manifest() {
520         let manifest = VersionManifest::new();
521         let mut version = Version::from("1.2.3").unwrap();
522 
523         version.manifest = Some(&manifest);
524         assert_eq!(version.manifest(), Some(&manifest));
525 
526         version.manifest = None;
527         assert_eq!(version.manifest(), None);
528     }
529 
530     #[test]
has_manifest()531     fn has_manifest() {
532         let manifest = VersionManifest::new();
533         let mut version = Version::from("1.2.3").unwrap();
534 
535         version.manifest = Some(&manifest);
536         assert!(version.has_manifest());
537 
538         version.manifest = None;
539         assert!(!version.has_manifest());
540     }
541 
542     #[test]
set_manifest()543     fn set_manifest() {
544         let manifest = VersionManifest::new();
545         let mut version = Version::from("1.2.3").unwrap();
546 
547         version.set_manifest(Some(&manifest));
548         assert_eq!(version.manifest, Some(&manifest));
549 
550         version.set_manifest(None);
551         assert_eq!(version.manifest, None);
552     }
553 
554     #[test]
as_str()555     fn as_str() {
556         // Test for each test version
557         for version in TEST_VERSIONS {
558             // The input version string must be the same as the returned string
559             assert_eq!(Version::from(&version.0).unwrap().as_str(), version.0);
560         }
561     }
562 
563     #[test]
part()564     fn part() {
565         // Test for each test version
566         for version in TEST_VERSIONS {
567             // Create a version object
568             let ver = Version::from(&version.0).unwrap();
569 
570             // Loop through each part
571             for i in 0..version.1 {
572                 assert_eq!(ver.part(i), Ok(&ver.parts[i]));
573             }
574 
575             // A value outside the range must return an error
576             assert!(ver.part(version.1).is_err());
577         }
578     }
579 
580     #[test]
parts()581     fn parts() {
582         // Test for each test version
583         for version in TEST_VERSIONS {
584             // The number of parts must match
585             assert_eq!(Version::from(&version.0).unwrap().parts().len(), version.1);
586         }
587     }
588 
589     #[test]
parts_max_depth()590     fn parts_max_depth() {
591         // Create a manifest
592         let mut manifest = VersionManifest::new();
593 
594         // Loop through a range of numbers
595         for depth in 0..5 {
596             // Set the maximum depth
597             manifest.set_max_depth_number(depth);
598 
599             // Test for each test version with the manifest
600             for version in TEST_VERSIONS {
601                 // Create a version object, and count it's parts
602                 let ver = Version::from_manifest(&version.0, &manifest);
603 
604                 // Some versions might be none, because not all of the start with a number when the
605                 // maximum depth is 1. A version string with only text isn't allowed,
606                 // resulting in none.
607                 if ver.is_none() {
608                     continue;
609                 }
610 
611                 // Get the part count
612                 let count = ver.unwrap().parts().len();
613 
614                 // The number of parts must match
615                 if depth == 0 {
616                     assert_eq!(count, version.1);
617                 } else {
618                     assert_eq!(count, cmp::min(version.1, depth));
619                 }
620             }
621         }
622     }
623 
624     #[test]
parts_ignore_text()625     fn parts_ignore_text() {
626         // Create a manifest
627         let mut manifest = VersionManifest::new();
628 
629         // Try this for true and false
630         for ignore in vec![true, false] {
631             // Set to ignore text
632             manifest.set_ignore_text(ignore);
633 
634             // Keep track whether any version passed with text
635             let mut had_text = false;
636 
637             // Test each test version
638             for version in TEST_VERSIONS {
639                 // Create a version instance, and get it's parts
640                 let ver = Version::from_manifest(&version.0, &manifest).unwrap();
641 
642                 // Loop through all version parts
643                 for part in ver.parts() {
644                     match part {
645                         &VersionPart::Text(_) => {
646                             // Set the flag
647                             had_text = true;
648 
649                             // Break the loop if we already reached text when not ignored
650                             if !ignore {
651                                 break;
652                             }
653                         }
654                         _ => {}
655                     }
656                 }
657             }
658 
659             // Assert had text
660             assert_eq!(had_text, !ignore);
661         }
662     }
663 
664     #[test]
part_count()665     fn part_count() {
666         // Test for each test version
667         for version in TEST_VERSIONS {
668             // The number of parts must match the metadata
669             assert_eq!(Version::from(&version.0).unwrap().part_count(), version.1);
670         }
671     }
672 
673     #[test]
compare()674     fn compare() {
675         // Compare each version in the version set
676         for entry in TEST_VERSION_SETS {
677             // Get both versions
678             let version_a = Version::from(&entry.0).unwrap();
679             let version_b = Version::from(&entry.1).unwrap();
680 
681             // Compare them
682             assert_eq!(
683                 version_a.compare(&version_b),
684                 entry.2.clone(),
685                 "Testing that {} is {} {}",
686                 &entry.0,
687                 &entry.2.sign(),
688                 &entry.1
689             );
690         }
691     }
692 
693     #[test]
compare_to()694     fn compare_to() {
695         // Compare each version in the version set
696         for entry in TEST_VERSION_SETS {
697             // Get both versions
698             let version_a = Version::from(&entry.0).unwrap();
699             let version_b = Version::from(&entry.1).unwrap();
700 
701             // Test
702             assert!(version_a.compare_to(&version_b, &entry.2));
703 
704             // Make sure the inverse operator is not correct
705             assert_eq!(version_a.compare_to(&version_b, &entry.2.invert()), false);
706         }
707 
708         // Assert an exceptional case, compare to not equal
709         assert!(Version::from("1.2")
710             .unwrap()
711             .compare_to(&Version::from("1.2.3").unwrap(), &CompOp::Ne,));
712     }
713 
714     #[test]
display()715     fn display() {
716         assert_eq!(format!("{}", Version::from("1.2.3").unwrap()), "1.2.3");
717     }
718 
719     #[test]
debug()720     fn debug() {
721         assert_eq!(
722             format!("{:?}", Version::from("1.2.3").unwrap()),
723             "[Number(1), Number(2), Number(3)]",
724         );
725         assert_eq!(
726             format!("{:#?}", Version::from("1.2.3").unwrap()),
727             "[\n    Number(\n        1,\n    ),\n    Number(\n        2,\n    ),\n    Number(\n        3,\n    ),\n]",
728         );
729     }
730 
731     #[test]
partial_cmp()732     fn partial_cmp() {
733         // Compare each version in the version set
734         for entry in TEST_VERSION_SETS {
735             // Get both versions
736             let version_a = Version::from(&entry.0).unwrap();
737             let version_b = Version::from(&entry.1).unwrap();
738 
739             // Compare and assert
740             match entry.2 {
741                 CompOp::Eq => assert!(version_a == version_b),
742                 CompOp::Lt => assert!(version_a < version_b),
743                 CompOp::Gt => assert!(version_a > version_b),
744                 _ => {}
745             }
746         }
747     }
748 
749     #[test]
partial_eq()750     fn partial_eq() {
751         // Compare each version in the version set
752         for entry in TEST_VERSION_SETS {
753             // Skip entries that are less or equal, or greater or equal
754             match entry.2 {
755                 CompOp::Le | CompOp::Ge => continue,
756                 _ => {}
757             }
758 
759             // Get both versions
760             let version_a = Version::from(&entry.0).unwrap();
761             let version_b = Version::from(&entry.1).unwrap();
762 
763             // Determine what the result should be
764             let result = match entry.2 {
765                 CompOp::Eq => true,
766                 _ => false,
767             };
768 
769             // Test
770             assert_eq!(version_a == version_b, result);
771         }
772 
773         // Assert an exceptional case, compare to not equal
774         assert!(Version::from("1.2").unwrap() != Version::from("1.2.3").unwrap());
775     }
776 }
777