16acc7838Sopenharmony_ciuse std::fmt; 26acc7838Sopenharmony_ci 36acc7838Sopenharmony_ci/// Version number: `major.minor.patch`, ignoring release channel. 46acc7838Sopenharmony_ci#[derive(PartialEq, Eq, Copy, Clone, PartialOrd, Ord)] 56acc7838Sopenharmony_cipub struct Version(u64); 66acc7838Sopenharmony_ci 76acc7838Sopenharmony_ciimpl Version { 86acc7838Sopenharmony_ci /// Reads the version of the running compiler. If it cannot be determined 96acc7838Sopenharmony_ci /// (see the [top-level documentation](crate)), returns `None`. 106acc7838Sopenharmony_ci /// 116acc7838Sopenharmony_ci /// # Example 126acc7838Sopenharmony_ci /// 136acc7838Sopenharmony_ci /// ```rust 146acc7838Sopenharmony_ci /// use version_check::Version; 156acc7838Sopenharmony_ci /// 166acc7838Sopenharmony_ci /// match Version::read() { 176acc7838Sopenharmony_ci /// Some(d) => format!("Version is: {}", d), 186acc7838Sopenharmony_ci /// None => format!("Failed to read the version.") 196acc7838Sopenharmony_ci /// }; 206acc7838Sopenharmony_ci /// ``` 216acc7838Sopenharmony_ci pub fn read() -> Option<Version> { 226acc7838Sopenharmony_ci ::get_version_and_date() 236acc7838Sopenharmony_ci .and_then(|(version, _)| version) 246acc7838Sopenharmony_ci .and_then(|version| Version::parse(&version)) 256acc7838Sopenharmony_ci } 266acc7838Sopenharmony_ci 276acc7838Sopenharmony_ci 286acc7838Sopenharmony_ci /// Parse a Rust release version (of the form 296acc7838Sopenharmony_ci /// `major[.minor[.patch[-channel]]]`), ignoring the release channel, if 306acc7838Sopenharmony_ci /// any. Returns `None` if `version` is not a valid Rust version string. 316acc7838Sopenharmony_ci /// 326acc7838Sopenharmony_ci /// # Example 336acc7838Sopenharmony_ci /// 346acc7838Sopenharmony_ci /// ```rust 356acc7838Sopenharmony_ci /// use version_check::Version; 366acc7838Sopenharmony_ci /// 376acc7838Sopenharmony_ci /// let version = Version::parse("1.18.0").unwrap(); 386acc7838Sopenharmony_ci /// assert!(version.exactly("1.18.0")); 396acc7838Sopenharmony_ci /// 406acc7838Sopenharmony_ci /// let version = Version::parse("1.20.0-nightly").unwrap(); 416acc7838Sopenharmony_ci /// assert!(version.exactly("1.20.0")); 426acc7838Sopenharmony_ci /// assert!(version.exactly("1.20.0-beta")); 436acc7838Sopenharmony_ci /// 446acc7838Sopenharmony_ci /// let version = Version::parse("1.3").unwrap(); 456acc7838Sopenharmony_ci /// assert!(version.exactly("1.3.0")); 466acc7838Sopenharmony_ci /// 476acc7838Sopenharmony_ci /// let version = Version::parse("1").unwrap(); 486acc7838Sopenharmony_ci /// assert!(version.exactly("1.0.0")); 496acc7838Sopenharmony_ci /// 506acc7838Sopenharmony_ci /// assert!(Version::parse("one.two.three").is_none()); 516acc7838Sopenharmony_ci /// assert!(Version::parse("1.65536.2").is_none()); 526acc7838Sopenharmony_ci /// assert!(Version::parse("1. 2").is_none()); 536acc7838Sopenharmony_ci /// assert!(Version::parse("").is_none()); 546acc7838Sopenharmony_ci /// assert!(Version::parse("1.").is_none()); 556acc7838Sopenharmony_ci /// assert!(Version::parse("1.2.3.4").is_none()); 566acc7838Sopenharmony_ci /// ``` 576acc7838Sopenharmony_ci pub fn parse(version: &str) -> Option<Version> { 586acc7838Sopenharmony_ci let splits = version.split('-') 596acc7838Sopenharmony_ci .nth(0) 606acc7838Sopenharmony_ci .unwrap_or("") 616acc7838Sopenharmony_ci .split('.') 626acc7838Sopenharmony_ci .map(|s| s.parse::<u16>()); 636acc7838Sopenharmony_ci 646acc7838Sopenharmony_ci let mut mmp = [0u16; 3]; 656acc7838Sopenharmony_ci for (i, split) in splits.enumerate() { 666acc7838Sopenharmony_ci mmp[i] = match (i, split) { 676acc7838Sopenharmony_ci (3, _) | (_, Err(_)) => return None, 686acc7838Sopenharmony_ci (_, Ok(v)) => v, 696acc7838Sopenharmony_ci }; 706acc7838Sopenharmony_ci } 716acc7838Sopenharmony_ci 726acc7838Sopenharmony_ci let (maj, min, patch) = (mmp[0], mmp[1], mmp[2]); 736acc7838Sopenharmony_ci Some(Version::from_mmp(maj, min, patch)) 746acc7838Sopenharmony_ci } 756acc7838Sopenharmony_ci 766acc7838Sopenharmony_ci /// Creates a `Version` from `(major, minor, patch)` version components. 776acc7838Sopenharmony_ci /// 786acc7838Sopenharmony_ci /// # Example 796acc7838Sopenharmony_ci /// 806acc7838Sopenharmony_ci /// ```rust 816acc7838Sopenharmony_ci /// use version_check::Version; 826acc7838Sopenharmony_ci /// 836acc7838Sopenharmony_ci /// assert!(Version::from_mmp(1, 35, 0).exactly("1.35.0")); 846acc7838Sopenharmony_ci /// assert!(Version::from_mmp(1, 33, 0).exactly("1.33.0")); 856acc7838Sopenharmony_ci /// assert!(Version::from_mmp(1, 35, 1).exactly("1.35.1")); 866acc7838Sopenharmony_ci /// assert!(Version::from_mmp(1, 13, 2).exactly("1.13.2")); 876acc7838Sopenharmony_ci /// ``` 886acc7838Sopenharmony_ci pub fn from_mmp(major: u16, minor: u16, patch: u16) -> Version { 896acc7838Sopenharmony_ci Version(((major as u64) << 32) | ((minor as u64) << 16) | patch as u64) 906acc7838Sopenharmony_ci } 916acc7838Sopenharmony_ci 926acc7838Sopenharmony_ci /// Returns the `(major, minor, patch)` version components of `self`. 936acc7838Sopenharmony_ci /// 946acc7838Sopenharmony_ci /// # Example 956acc7838Sopenharmony_ci /// 966acc7838Sopenharmony_ci /// ```rust 976acc7838Sopenharmony_ci /// use version_check::Version; 986acc7838Sopenharmony_ci /// 996acc7838Sopenharmony_ci /// assert_eq!(Version::parse("1.35.0").unwrap().to_mmp(), (1, 35, 0)); 1006acc7838Sopenharmony_ci /// assert_eq!(Version::parse("1.33.0").unwrap().to_mmp(), (1, 33, 0)); 1016acc7838Sopenharmony_ci /// assert_eq!(Version::parse("1.35.1").unwrap().to_mmp(), (1, 35, 1)); 1026acc7838Sopenharmony_ci /// assert_eq!(Version::parse("1.13.2").unwrap().to_mmp(), (1, 13, 2)); 1036acc7838Sopenharmony_ci /// ``` 1046acc7838Sopenharmony_ci pub fn to_mmp(&self) -> (u16, u16, u16) { 1056acc7838Sopenharmony_ci let major = self.0 >> 32; 1066acc7838Sopenharmony_ci let minor = self.0 >> 16; 1076acc7838Sopenharmony_ci let patch = self.0; 1086acc7838Sopenharmony_ci (major as u16, minor as u16, patch as u16) 1096acc7838Sopenharmony_ci } 1106acc7838Sopenharmony_ci 1116acc7838Sopenharmony_ci /// Returns `true` if `self` is greater than or equal to `version`. 1126acc7838Sopenharmony_ci /// 1136acc7838Sopenharmony_ci /// If `version` is greater than `self`, or if `version` is not a valid Rust 1146acc7838Sopenharmony_ci /// version string, returns `false`. 1156acc7838Sopenharmony_ci /// 1166acc7838Sopenharmony_ci /// # Example 1176acc7838Sopenharmony_ci /// 1186acc7838Sopenharmony_ci /// ```rust 1196acc7838Sopenharmony_ci /// use version_check::Version; 1206acc7838Sopenharmony_ci /// 1216acc7838Sopenharmony_ci /// let version = Version::parse("1.35.0").unwrap(); 1226acc7838Sopenharmony_ci /// 1236acc7838Sopenharmony_ci /// assert!(version.at_least("1.33.0")); 1246acc7838Sopenharmony_ci /// assert!(version.at_least("1.35.0")); 1256acc7838Sopenharmony_ci /// assert!(version.at_least("1.13.2")); 1266acc7838Sopenharmony_ci /// 1276acc7838Sopenharmony_ci /// assert!(!version.at_least("1.35.1")); 1286acc7838Sopenharmony_ci /// assert!(!version.at_least("1.55.0")); 1296acc7838Sopenharmony_ci /// 1306acc7838Sopenharmony_ci /// let version = Version::parse("1.12.5").unwrap(); 1316acc7838Sopenharmony_ci /// 1326acc7838Sopenharmony_ci /// assert!(version.at_least("1.12.0")); 1336acc7838Sopenharmony_ci /// assert!(!version.at_least("1.35.0")); 1346acc7838Sopenharmony_ci /// ``` 1356acc7838Sopenharmony_ci pub fn at_least(&self, version: &str) -> bool { 1366acc7838Sopenharmony_ci Version::parse(version) 1376acc7838Sopenharmony_ci .map(|version| self >= &version) 1386acc7838Sopenharmony_ci .unwrap_or(false) 1396acc7838Sopenharmony_ci } 1406acc7838Sopenharmony_ci 1416acc7838Sopenharmony_ci /// Returns `true` if `self` is less than or equal to `version`. 1426acc7838Sopenharmony_ci /// 1436acc7838Sopenharmony_ci /// If `version` is less than `self`, or if `version` is not a valid Rust 1446acc7838Sopenharmony_ci /// version string, returns `false`. 1456acc7838Sopenharmony_ci /// 1466acc7838Sopenharmony_ci /// # Example 1476acc7838Sopenharmony_ci /// 1486acc7838Sopenharmony_ci /// ```rust 1496acc7838Sopenharmony_ci /// use version_check::Version; 1506acc7838Sopenharmony_ci /// 1516acc7838Sopenharmony_ci /// let version = Version::parse("1.35.0").unwrap(); 1526acc7838Sopenharmony_ci /// 1536acc7838Sopenharmony_ci /// assert!(version.at_most("1.35.1")); 1546acc7838Sopenharmony_ci /// assert!(version.at_most("1.55.0")); 1556acc7838Sopenharmony_ci /// assert!(version.at_most("1.35.0")); 1566acc7838Sopenharmony_ci /// 1576acc7838Sopenharmony_ci /// assert!(!version.at_most("1.33.0")); 1586acc7838Sopenharmony_ci /// assert!(!version.at_most("1.13.2")); 1596acc7838Sopenharmony_ci /// ``` 1606acc7838Sopenharmony_ci pub fn at_most(&self, version: &str) -> bool { 1616acc7838Sopenharmony_ci Version::parse(version) 1626acc7838Sopenharmony_ci .map(|version| self <= &version) 1636acc7838Sopenharmony_ci .unwrap_or(false) 1646acc7838Sopenharmony_ci } 1656acc7838Sopenharmony_ci 1666acc7838Sopenharmony_ci /// Returns `true` if `self` is exactly equal to `version`. 1676acc7838Sopenharmony_ci /// 1686acc7838Sopenharmony_ci /// If `version` is not equal to `self`, or if `version` is not a valid Rust 1696acc7838Sopenharmony_ci /// version string, returns `false`. 1706acc7838Sopenharmony_ci /// 1716acc7838Sopenharmony_ci /// # Example 1726acc7838Sopenharmony_ci /// 1736acc7838Sopenharmony_ci /// ```rust 1746acc7838Sopenharmony_ci /// use version_check::Version; 1756acc7838Sopenharmony_ci /// 1766acc7838Sopenharmony_ci /// let version = Version::parse("1.35.0").unwrap(); 1776acc7838Sopenharmony_ci /// 1786acc7838Sopenharmony_ci /// assert!(version.exactly("1.35.0")); 1796acc7838Sopenharmony_ci /// 1806acc7838Sopenharmony_ci /// assert!(!version.exactly("1.33.0")); 1816acc7838Sopenharmony_ci /// assert!(!version.exactly("1.35.1")); 1826acc7838Sopenharmony_ci /// assert!(!version.exactly("1.13.2")); 1836acc7838Sopenharmony_ci /// ``` 1846acc7838Sopenharmony_ci pub fn exactly(&self, version: &str) -> bool { 1856acc7838Sopenharmony_ci Version::parse(version) 1866acc7838Sopenharmony_ci .map(|version| self == &version) 1876acc7838Sopenharmony_ci .unwrap_or(false) 1886acc7838Sopenharmony_ci } 1896acc7838Sopenharmony_ci} 1906acc7838Sopenharmony_ci 1916acc7838Sopenharmony_ciimpl fmt::Display for Version { 1926acc7838Sopenharmony_ci fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 1936acc7838Sopenharmony_ci let (major, minor, patch) = self.to_mmp(); 1946acc7838Sopenharmony_ci write!(f, "{}.{}.{}", major, minor, patch) 1956acc7838Sopenharmony_ci } 1966acc7838Sopenharmony_ci} 1976acc7838Sopenharmony_ci 1986acc7838Sopenharmony_ciimpl fmt::Debug for Version { 1996acc7838Sopenharmony_ci fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 2006acc7838Sopenharmony_ci // We don't use `debug_*` because it's not available in `1.0.0`. 2016acc7838Sopenharmony_ci write!(f, "Version({:?}, {:?})", self.0, self.to_mmp()) 2026acc7838Sopenharmony_ci } 2036acc7838Sopenharmony_ci} 2046acc7838Sopenharmony_ci 2056acc7838Sopenharmony_ci#[cfg(test)] 2066acc7838Sopenharmony_cimod tests { 2076acc7838Sopenharmony_ci use super::Version; 2086acc7838Sopenharmony_ci 2096acc7838Sopenharmony_ci macro_rules! assert_to_mmp { 2106acc7838Sopenharmony_ci // We don't use `.into::<Option<_>>` because it's not available in 1.0. 2116acc7838Sopenharmony_ci // We don't use the message part of `assert!` for the same reason. 2126acc7838Sopenharmony_ci ($s:expr, None) => ( 2136acc7838Sopenharmony_ci assert_eq!(Version::parse($s), None); 2146acc7838Sopenharmony_ci ); 2156acc7838Sopenharmony_ci ($s:expr, $mmp:expr) => ( 2166acc7838Sopenharmony_ci assert_eq!(Version::parse($s).map(|v| v.to_mmp()), Some($mmp)); 2176acc7838Sopenharmony_ci ) 2186acc7838Sopenharmony_ci } 2196acc7838Sopenharmony_ci 2206acc7838Sopenharmony_ci macro_rules! assert_from_mmp { 2216acc7838Sopenharmony_ci (($x:expr, $y:expr, $z:expr) => $s:expr) => { 2226acc7838Sopenharmony_ci assert_eq!(Some(Version::from_mmp($x, $y, $z)), Version::parse($s)); 2236acc7838Sopenharmony_ci }; 2246acc7838Sopenharmony_ci } 2256acc7838Sopenharmony_ci 2266acc7838Sopenharmony_ci #[test] 2276acc7838Sopenharmony_ci fn test_str_to_mmp() { 2286acc7838Sopenharmony_ci assert_to_mmp!("1", (1, 0, 0)); 2296acc7838Sopenharmony_ci assert_to_mmp!("1.2", (1, 2, 0)); 2306acc7838Sopenharmony_ci assert_to_mmp!("1.18.0", (1, 18, 0)); 2316acc7838Sopenharmony_ci assert_to_mmp!("3.19.0", (3, 19, 0)); 2326acc7838Sopenharmony_ci assert_to_mmp!("1.19.0-nightly", (1, 19, 0)); 2336acc7838Sopenharmony_ci assert_to_mmp!("1.12.2349", (1, 12, 2349)); 2346acc7838Sopenharmony_ci assert_to_mmp!("0.12", (0, 12, 0)); 2356acc7838Sopenharmony_ci assert_to_mmp!("1.12.5", (1, 12, 5)); 2366acc7838Sopenharmony_ci assert_to_mmp!("1.12", (1, 12, 0)); 2376acc7838Sopenharmony_ci assert_to_mmp!("1", (1, 0, 0)); 2386acc7838Sopenharmony_ci assert_to_mmp!("1.4.4-nightly (d84693b93 2017-07-09))", (1, 4, 4)); 2396acc7838Sopenharmony_ci assert_to_mmp!("1.58879.4478-dev", (1, 58879, 4478)); 2406acc7838Sopenharmony_ci assert_to_mmp!("1.58879.4478-dev (d84693b93 2017-07-09))", (1, 58879, 4478)); 2416acc7838Sopenharmony_ci } 2426acc7838Sopenharmony_ci 2436acc7838Sopenharmony_ci #[test] 2446acc7838Sopenharmony_ci fn test_malformed() { 2456acc7838Sopenharmony_ci assert_to_mmp!("1.65536.2", None); 2466acc7838Sopenharmony_ci assert_to_mmp!("-1.2.3", None); 2476acc7838Sopenharmony_ci assert_to_mmp!("1. 2", None); 2486acc7838Sopenharmony_ci assert_to_mmp!("", None); 2496acc7838Sopenharmony_ci assert_to_mmp!(" ", None); 2506acc7838Sopenharmony_ci assert_to_mmp!(".", None); 2516acc7838Sopenharmony_ci assert_to_mmp!("one", None); 2526acc7838Sopenharmony_ci assert_to_mmp!("1.", None); 2536acc7838Sopenharmony_ci assert_to_mmp!("1.2.3.4.5.6", None); 2546acc7838Sopenharmony_ci } 2556acc7838Sopenharmony_ci 2566acc7838Sopenharmony_ci #[test] 2576acc7838Sopenharmony_ci fn test_from_mmp() { 2586acc7838Sopenharmony_ci assert_from_mmp!((1, 18, 0) => "1.18.0"); 2596acc7838Sopenharmony_ci assert_from_mmp!((3, 19, 0) => "3.19.0"); 2606acc7838Sopenharmony_ci assert_from_mmp!((1, 19, 0) => "1.19.0"); 2616acc7838Sopenharmony_ci assert_from_mmp!((1, 12, 2349) => "1.12.2349"); 2626acc7838Sopenharmony_ci assert_from_mmp!((0, 12, 0) => "0.12"); 2636acc7838Sopenharmony_ci assert_from_mmp!((1, 12, 5) => "1.12.5"); 2646acc7838Sopenharmony_ci assert_from_mmp!((1, 12, 0) => "1.12"); 2656acc7838Sopenharmony_ci assert_from_mmp!((1, 0, 0) => "1"); 2666acc7838Sopenharmony_ci assert_from_mmp!((1, 4, 4) => "1.4.4"); 2676acc7838Sopenharmony_ci assert_from_mmp!((1, 58879, 4478) => "1.58879.4478"); 2686acc7838Sopenharmony_ci } 2696acc7838Sopenharmony_ci 2706acc7838Sopenharmony_ci #[test] 2716acc7838Sopenharmony_ci fn test_comparisons() { 2726acc7838Sopenharmony_ci let version = Version::parse("1.18.0").unwrap(); 2736acc7838Sopenharmony_ci assert!(version.exactly("1.18.0")); 2746acc7838Sopenharmony_ci assert!(version.at_least("1.12.0")); 2756acc7838Sopenharmony_ci assert!(version.at_least("1.12")); 2766acc7838Sopenharmony_ci assert!(version.at_least("1")); 2776acc7838Sopenharmony_ci assert!(version.at_most("1.18.1")); 2786acc7838Sopenharmony_ci assert!(!version.exactly("1.19.0")); 2796acc7838Sopenharmony_ci assert!(!version.exactly("1.18.1")); 2806acc7838Sopenharmony_ci 2816acc7838Sopenharmony_ci let version = Version::parse("1.20.0-nightly").unwrap(); 2826acc7838Sopenharmony_ci assert!(version.exactly("1.20.0-beta")); 2836acc7838Sopenharmony_ci assert!(version.exactly("1.20.0-nightly")); 2846acc7838Sopenharmony_ci assert!(version.exactly("1.20.0")); 2856acc7838Sopenharmony_ci assert!(!version.exactly("1.19")); 2866acc7838Sopenharmony_ci 2876acc7838Sopenharmony_ci let version = Version::parse("1.3").unwrap(); 2886acc7838Sopenharmony_ci assert!(version.exactly("1.3.0")); 2896acc7838Sopenharmony_ci assert!(version.exactly("1.3.0-stable")); 2906acc7838Sopenharmony_ci assert!(version.exactly("1.3")); 2916acc7838Sopenharmony_ci assert!(!version.exactly("1.5.0-stable")); 2926acc7838Sopenharmony_ci 2936acc7838Sopenharmony_ci let version = Version::parse("1").unwrap(); 2946acc7838Sopenharmony_ci assert!(version.exactly("1.0.0")); 2956acc7838Sopenharmony_ci assert!(version.exactly("1.0")); 2966acc7838Sopenharmony_ci assert!(version.exactly("1")); 2976acc7838Sopenharmony_ci 2986acc7838Sopenharmony_ci assert!(Version::parse("one.two.three").is_none()); 2996acc7838Sopenharmony_ci } 3006acc7838Sopenharmony_ci 3016acc7838Sopenharmony_ci macro_rules! reflexive_display { 3026acc7838Sopenharmony_ci ($s:expr) => ( 3036acc7838Sopenharmony_ci assert_eq!(Version::parse($s).unwrap().to_string(), $s); 3046acc7838Sopenharmony_ci ) 3056acc7838Sopenharmony_ci } 3066acc7838Sopenharmony_ci 3076acc7838Sopenharmony_ci #[test] 3086acc7838Sopenharmony_ci fn display() { 3096acc7838Sopenharmony_ci reflexive_display!("1.0.0"); 3106acc7838Sopenharmony_ci reflexive_display!("1.2.3"); 3116acc7838Sopenharmony_ci reflexive_display!("1.12.1438"); 3126acc7838Sopenharmony_ci reflexive_display!("1.44.0"); 3136acc7838Sopenharmony_ci reflexive_display!("2.44.0"); 3146acc7838Sopenharmony_ci reflexive_display!("23459.28923.3483"); 3156acc7838Sopenharmony_ci } 3166acc7838Sopenharmony_ci} 317