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