use crate::{Error, Time};
pub(crate) trait CheckedTime {
fn year(&self) -> i32;
fn month(&self) -> Result<u8, Error>;
fn day(&self) -> Result<u8, Error>;
fn hour(&self) -> Result<u8, Error>;
fn minute(&self) -> Result<u8, Error>;
fn second(&self) -> Result<u8, Error>;
fn nanoseconds(&self) -> Result<u32, Error>;
fn day_of_week(&self) -> Result<u8, Error>;
fn day_of_year(&self) -> Result<u16, Error>;
fn to_int(&self) -> i64;
fn is_utc(&self) -> bool;
fn utc_offset(&self) -> i32;
fn time_zone(&self) -> Result<&str, Error>;
}
impl<T: Time> CheckedTime for T {
fn year(&self) -> i32 {
self.year()
}
fn month(&self) -> Result<u8, Error> {
match self.month() {
month @ 1..=12 => Ok(month),
_ => Err(Error::InvalidTime),
}
}
fn day(&self) -> Result<u8, Error> {
match self.day() {
day @ 1..=31 => Ok(day),
_ => Err(Error::InvalidTime),
}
}
fn hour(&self) -> Result<u8, Error> {
match self.hour() {
hour @ 0..=23 => Ok(hour),
_ => Err(Error::InvalidTime),
}
}
fn minute(&self) -> Result<u8, Error> {
match self.minute() {
minute @ 0..=59 => Ok(minute),
_ => Err(Error::InvalidTime),
}
}
fn second(&self) -> Result<u8, Error> {
match self.second() {
second @ 0..=60 => Ok(second),
_ => Err(Error::InvalidTime),
}
}
fn nanoseconds(&self) -> Result<u32, Error> {
match self.nanoseconds() {
nanoseconds @ 0..=999_999_999 => Ok(nanoseconds),
_ => Err(Error::InvalidTime),
}
}
fn day_of_week(&self) -> Result<u8, Error> {
match self.day_of_week() {
day_of_week @ 0..=6 => Ok(day_of_week),
_ => Err(Error::InvalidTime),
}
}
fn day_of_year(&self) -> Result<u16, Error> {
match self.day_of_year() {
day_of_year @ 1..=366 => Ok(day_of_year),
_ => Err(Error::InvalidTime),
}
}
fn to_int(&self) -> i64 {
self.to_int()
}
fn is_utc(&self) -> bool {
self.is_utc()
}
fn utc_offset(&self) -> i32 {
self.utc_offset()
}
fn time_zone(&self) -> Result<&str, Error> {
match self.time_zone() {
time_zone if time_zone.is_ascii() => Ok(time_zone),
_ => Err(Error::InvalidTime),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
include!("../mock.rs.in");
fn check<T>(ok: bool, result: &Result<T, Error>) {
if ok {
assert!(matches!(result, Ok(_)));
} else {
assert!(matches!(result, Err(Error::InvalidTime)));
}
}
#[test]
fn test_checked_time() {
#[rustfmt::skip]
let times = [
MockTime::new(1970, 1, 1, 0, 0, 0, 0, 4, 1, 0, false, 0, ""),
MockTime::new(1970, 0, 0, 99, 99, 99, 1_000_000_000, 9, 999, 0, false, 0, "€"),
];
check(true, &CheckedTime::month(×[0]));
check(true, &CheckedTime::day(×[0]));
check(true, &CheckedTime::hour(×[0]));
check(true, &CheckedTime::minute(×[0]));
check(true, &CheckedTime::second(×[0]));
check(true, &CheckedTime::nanoseconds(×[0]));
check(true, &CheckedTime::day_of_week(×[0]));
check(true, &CheckedTime::day_of_year(×[0]));
check(true, &CheckedTime::time_zone(×[0]));
check(false, &CheckedTime::month(×[1]));
check(false, &CheckedTime::day(×[1]));
check(false, &CheckedTime::hour(×[1]));
check(false, &CheckedTime::minute(×[1]));
check(false, &CheckedTime::second(×[1]));
check(false, &CheckedTime::nanoseconds(×[1]));
check(false, &CheckedTime::day_of_week(×[1]));
check(false, &CheckedTime::day_of_year(×[1]));
check(false, &CheckedTime::time_zone(×[1]));
}
}