16acc7838Sopenharmony_ciuse std::fmt;
26acc7838Sopenharmony_ci
36acc7838Sopenharmony_ci/// Release date including year, month, and day.
46acc7838Sopenharmony_ci// Internal storage is: y[31..9] | m[8..5] | d[5...0].
56acc7838Sopenharmony_ci#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
66acc7838Sopenharmony_cipub struct Date(u32);
76acc7838Sopenharmony_ci
86acc7838Sopenharmony_ciimpl Date {
96acc7838Sopenharmony_ci    /// Reads the release date of the running compiler. If it cannot be
106acc7838Sopenharmony_ci    /// determined (see the [top-level documentation](crate)), returns `None`.
116acc7838Sopenharmony_ci    ///
126acc7838Sopenharmony_ci    /// # Example
136acc7838Sopenharmony_ci    ///
146acc7838Sopenharmony_ci    /// ```rust
156acc7838Sopenharmony_ci    /// use version_check::Date;
166acc7838Sopenharmony_ci    ///
176acc7838Sopenharmony_ci    /// match Date::read() {
186acc7838Sopenharmony_ci    ///     Some(d) => format!("The release date is: {}", d),
196acc7838Sopenharmony_ci    ///     None => format!("Failed to read the release date.")
206acc7838Sopenharmony_ci    /// };
216acc7838Sopenharmony_ci    /// ```
226acc7838Sopenharmony_ci    pub fn read() -> Option<Date> {
236acc7838Sopenharmony_ci        ::get_version_and_date()
246acc7838Sopenharmony_ci            .and_then(|(_, date)| date)
256acc7838Sopenharmony_ci            .and_then(|date| Date::parse(&date))
266acc7838Sopenharmony_ci    }
276acc7838Sopenharmony_ci
286acc7838Sopenharmony_ci    /// Parse a release date of the form `%Y-%m-%d`. Returns `None` if `date` is
296acc7838Sopenharmony_ci    /// not in `%Y-%m-%d` format.
306acc7838Sopenharmony_ci    ///
316acc7838Sopenharmony_ci    /// # Example
326acc7838Sopenharmony_ci    ///
336acc7838Sopenharmony_ci    /// ```rust
346acc7838Sopenharmony_ci    /// use version_check::Date;
356acc7838Sopenharmony_ci    ///
366acc7838Sopenharmony_ci    /// let date = Date::parse("2016-04-20").unwrap();
376acc7838Sopenharmony_ci    ///
386acc7838Sopenharmony_ci    /// assert!(date.at_least("2016-01-10"));
396acc7838Sopenharmony_ci    /// assert!(date.at_most("2016-04-20"));
406acc7838Sopenharmony_ci    /// assert!(date.exactly("2016-04-20"));
416acc7838Sopenharmony_ci    ///
426acc7838Sopenharmony_ci    /// assert!(Date::parse("2021-12-31").unwrap().exactly("2021-12-31"));
436acc7838Sopenharmony_ci    ///
446acc7838Sopenharmony_ci    /// assert!(Date::parse("March 13, 2018").is_none());
456acc7838Sopenharmony_ci    /// assert!(Date::parse("1-2-3-4-5").is_none());
466acc7838Sopenharmony_ci    /// assert!(Date::parse("2020-300-23120").is_none());
476acc7838Sopenharmony_ci    /// assert!(Date::parse("2020-12-12 1").is_none());
486acc7838Sopenharmony_ci    /// assert!(Date::parse("2020-10").is_none());
496acc7838Sopenharmony_ci    /// assert!(Date::parse("2020").is_none());
506acc7838Sopenharmony_ci    /// ```
516acc7838Sopenharmony_ci    pub fn parse(date: &str) -> Option<Date> {
526acc7838Sopenharmony_ci        let mut ymd = [0u16; 3];
536acc7838Sopenharmony_ci        for (i, split) in date.split('-').map(|s| s.parse::<u16>()).enumerate() {
546acc7838Sopenharmony_ci            ymd[i] = match (i, split) {
556acc7838Sopenharmony_ci                (3, _) | (_, Err(_)) => return None,
566acc7838Sopenharmony_ci                (_, Ok(v)) => v,
576acc7838Sopenharmony_ci            };
586acc7838Sopenharmony_ci        }
596acc7838Sopenharmony_ci
606acc7838Sopenharmony_ci        let (year, month, day) = (ymd[0], ymd[1], ymd[2]);
616acc7838Sopenharmony_ci        if year == 0 || month == 0 || month > 12 || day == 0 || day > 31 {
626acc7838Sopenharmony_ci            return None;
636acc7838Sopenharmony_ci        }
646acc7838Sopenharmony_ci
656acc7838Sopenharmony_ci        Some(Date::from_ymd(year, month as u8, day as u8))
666acc7838Sopenharmony_ci    }
676acc7838Sopenharmony_ci
686acc7838Sopenharmony_ci    /// Creates a `Date` from `(year, month, day)` date components.
696acc7838Sopenharmony_ci    ///
706acc7838Sopenharmony_ci    /// Does not check the validity of `year`, `month`, or `day`, but `year` is
716acc7838Sopenharmony_ci    /// truncated to 23 bits (% 8,388,608), `month` to 4 bits (% 16), and `day`
726acc7838Sopenharmony_ci    /// to 5 bits (% 32).
736acc7838Sopenharmony_ci    ///
746acc7838Sopenharmony_ci    /// # Example
756acc7838Sopenharmony_ci    ///
766acc7838Sopenharmony_ci    /// ```rust
776acc7838Sopenharmony_ci    /// use version_check::Date;
786acc7838Sopenharmony_ci    ///
796acc7838Sopenharmony_ci    /// assert!(Date::from_ymd(2021, 7, 30).exactly("2021-07-30"));
806acc7838Sopenharmony_ci    /// assert!(Date::from_ymd(2010, 3, 23).exactly("2010-03-23"));
816acc7838Sopenharmony_ci    /// assert!(Date::from_ymd(2090, 1, 31).exactly("2090-01-31"));
826acc7838Sopenharmony_ci    ///
836acc7838Sopenharmony_ci    /// // Truncation: 33 % 32 == 0x21 & 0x1F == 1.
846acc7838Sopenharmony_ci    /// assert!(Date::from_ymd(2090, 1, 33).exactly("2090-01-01"));
856acc7838Sopenharmony_ci    /// ```
866acc7838Sopenharmony_ci    pub fn from_ymd(year: u16, month: u8, day: u8) -> Date {
876acc7838Sopenharmony_ci        let year = (year as u32) << 9;
886acc7838Sopenharmony_ci        let month = ((month as u32) & 0xF) << 5;
896acc7838Sopenharmony_ci        let day = (day as u32) & 0x1F;
906acc7838Sopenharmony_ci        Date(year | month | day)
916acc7838Sopenharmony_ci    }
926acc7838Sopenharmony_ci
936acc7838Sopenharmony_ci    /// Return the original (YYYY, MM, DD).
946acc7838Sopenharmony_ci    fn to_ymd(&self) -> (u16, u8, u8) {
956acc7838Sopenharmony_ci        let y = self.0 >> 9;
966acc7838Sopenharmony_ci        let m = (self.0 >> 5) & 0xF;
976acc7838Sopenharmony_ci        let d = self.0 & 0x1F;
986acc7838Sopenharmony_ci        (y as u16, m as u8, d as u8)
996acc7838Sopenharmony_ci    }
1006acc7838Sopenharmony_ci
1016acc7838Sopenharmony_ci    /// Returns `true` if `self` occurs on or after `date`.
1026acc7838Sopenharmony_ci    ///
1036acc7838Sopenharmony_ci    /// If `date` occurs before `self`, or if `date` is not in `%Y-%m-%d`
1046acc7838Sopenharmony_ci    /// format, returns `false`.
1056acc7838Sopenharmony_ci    ///
1066acc7838Sopenharmony_ci    /// # Example
1076acc7838Sopenharmony_ci    ///
1086acc7838Sopenharmony_ci    /// ```rust
1096acc7838Sopenharmony_ci    /// use version_check::Date;
1106acc7838Sopenharmony_ci    ///
1116acc7838Sopenharmony_ci    /// let date = Date::parse("2020-01-01").unwrap();
1126acc7838Sopenharmony_ci    ///
1136acc7838Sopenharmony_ci    /// assert!(date.at_least("2019-12-31"));
1146acc7838Sopenharmony_ci    /// assert!(date.at_least("2020-01-01"));
1156acc7838Sopenharmony_ci    /// assert!(date.at_least("2014-04-31"));
1166acc7838Sopenharmony_ci    ///
1176acc7838Sopenharmony_ci    /// assert!(!date.at_least("2020-01-02"));
1186acc7838Sopenharmony_ci    /// assert!(!date.at_least("2024-08-18"));
1196acc7838Sopenharmony_ci    /// ```
1206acc7838Sopenharmony_ci    pub fn at_least(&self, date: &str) -> bool {
1216acc7838Sopenharmony_ci        Date::parse(date)
1226acc7838Sopenharmony_ci            .map(|date| self >= &date)
1236acc7838Sopenharmony_ci            .unwrap_or(false)
1246acc7838Sopenharmony_ci    }
1256acc7838Sopenharmony_ci
1266acc7838Sopenharmony_ci    /// Returns `true` if `self` occurs on or before `date`.
1276acc7838Sopenharmony_ci    ///
1286acc7838Sopenharmony_ci    /// If `date` occurs after `self`, or if `date` is not in `%Y-%m-%d`
1296acc7838Sopenharmony_ci    /// format, returns `false`.
1306acc7838Sopenharmony_ci    ///
1316acc7838Sopenharmony_ci    /// # Example
1326acc7838Sopenharmony_ci    ///
1336acc7838Sopenharmony_ci    /// ```rust
1346acc7838Sopenharmony_ci    /// use version_check::Date;
1356acc7838Sopenharmony_ci    ///
1366acc7838Sopenharmony_ci    /// let date = Date::parse("2020-01-01").unwrap();
1376acc7838Sopenharmony_ci    ///
1386acc7838Sopenharmony_ci    /// assert!(date.at_most("2020-01-01"));
1396acc7838Sopenharmony_ci    /// assert!(date.at_most("2020-01-02"));
1406acc7838Sopenharmony_ci    /// assert!(date.at_most("2024-08-18"));
1416acc7838Sopenharmony_ci    ///
1426acc7838Sopenharmony_ci    /// assert!(!date.at_most("2019-12-31"));
1436acc7838Sopenharmony_ci    /// assert!(!date.at_most("2014-04-31"));
1446acc7838Sopenharmony_ci    /// ```
1456acc7838Sopenharmony_ci    pub fn at_most(&self, date: &str) -> bool {
1466acc7838Sopenharmony_ci        Date::parse(date)
1476acc7838Sopenharmony_ci            .map(|date| self <= &date)
1486acc7838Sopenharmony_ci            .unwrap_or(false)
1496acc7838Sopenharmony_ci    }
1506acc7838Sopenharmony_ci
1516acc7838Sopenharmony_ci    /// Returns `true` if `self` occurs exactly on `date`.
1526acc7838Sopenharmony_ci    ///
1536acc7838Sopenharmony_ci    /// If `date` is not exactly `self`, or if `date` is not in `%Y-%m-%d`
1546acc7838Sopenharmony_ci    /// format, returns `false`.
1556acc7838Sopenharmony_ci    ///
1566acc7838Sopenharmony_ci    /// # Example
1576acc7838Sopenharmony_ci    ///
1586acc7838Sopenharmony_ci    /// ```rust
1596acc7838Sopenharmony_ci    /// use version_check::Date;
1606acc7838Sopenharmony_ci    ///
1616acc7838Sopenharmony_ci    /// let date = Date::parse("2020-01-01").unwrap();
1626acc7838Sopenharmony_ci    ///
1636acc7838Sopenharmony_ci    /// assert!(date.exactly("2020-01-01"));
1646acc7838Sopenharmony_ci    ///
1656acc7838Sopenharmony_ci    /// assert!(!date.exactly("2019-12-31"));
1666acc7838Sopenharmony_ci    /// assert!(!date.exactly("2014-04-31"));
1676acc7838Sopenharmony_ci    /// assert!(!date.exactly("2020-01-02"));
1686acc7838Sopenharmony_ci    /// assert!(!date.exactly("2024-08-18"));
1696acc7838Sopenharmony_ci    /// ```
1706acc7838Sopenharmony_ci    pub fn exactly(&self, date: &str) -> bool {
1716acc7838Sopenharmony_ci        Date::parse(date)
1726acc7838Sopenharmony_ci            .map(|date| self == &date)
1736acc7838Sopenharmony_ci            .unwrap_or(false)
1746acc7838Sopenharmony_ci    }
1756acc7838Sopenharmony_ci}
1766acc7838Sopenharmony_ci
1776acc7838Sopenharmony_ciimpl fmt::Display for Date {
1786acc7838Sopenharmony_ci    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1796acc7838Sopenharmony_ci        let (y, m, d) = self.to_ymd();
1806acc7838Sopenharmony_ci        write!(f, "{}-{:02}-{:02}", y, m, d)
1816acc7838Sopenharmony_ci    }
1826acc7838Sopenharmony_ci}
1836acc7838Sopenharmony_ci
1846acc7838Sopenharmony_ci#[cfg(test)]
1856acc7838Sopenharmony_cimod tests {
1866acc7838Sopenharmony_ci    use super::Date;
1876acc7838Sopenharmony_ci
1886acc7838Sopenharmony_ci    macro_rules! reflexive_display {
1896acc7838Sopenharmony_ci        ($string:expr) => (
1906acc7838Sopenharmony_ci            assert_eq!(Date::parse($string).unwrap().to_string(), $string);
1916acc7838Sopenharmony_ci        )
1926acc7838Sopenharmony_ci    }
1936acc7838Sopenharmony_ci
1946acc7838Sopenharmony_ci    #[test]
1956acc7838Sopenharmony_ci    fn display() {
1966acc7838Sopenharmony_ci        reflexive_display!("2019-05-08");
1976acc7838Sopenharmony_ci        reflexive_display!("2000-01-01");
1986acc7838Sopenharmony_ci        reflexive_display!("2000-12-31");
1996acc7838Sopenharmony_ci        reflexive_display!("2090-12-31");
2006acc7838Sopenharmony_ci        reflexive_display!("1999-02-19");
2016acc7838Sopenharmony_ci        reflexive_display!("9999-12-31");
2026acc7838Sopenharmony_ci    }
2036acc7838Sopenharmony_ci}
204