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