use std::{fmt, str::FromStr};
use chrono::TimeDelta;
use crate::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum Resolution {
Nanoseconds,
Microseconds,
Milliseconds,
Seconds,
Minutes,
Hours,
Days,
Weeks,
Months,
Years,
}
impl Resolution {
pub const ALL: &'static [Self] = &[Self::Nanoseconds, Self::Microseconds, Self::Milliseconds, Self::Seconds, Self::Minutes, Self::Hours, Self::Days, Self::Weeks, Self::Months, Self::Years];
#[must_use]
pub const fn step_nanos(self) -> i64 {
const SECOND: i64 = 1_000_000_000;
const DAY: i64 = 86_400 * SECOND;
match self {
Self::Nanoseconds => 1,
Self::Microseconds => 1_000,
Self::Milliseconds => 1_000_000,
Self::Seconds => SECOND,
Self::Minutes => 60 * SECOND,
Self::Hours => 3_600 * SECOND,
Self::Days => DAY,
Self::Weeks => 7 * DAY,
Self::Months => 30 * DAY,
Self::Years => 365 * DAY,
}
}
#[must_use]
pub const fn step(self) -> TimeDelta {
TimeDelta::nanoseconds(self.step_nanos())
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Nanoseconds => "nanoseconds",
Self::Microseconds => "microseconds",
Self::Milliseconds => "milliseconds",
Self::Seconds => "seconds",
Self::Minutes => "minutes",
Self::Hours => "hours",
Self::Days => "days",
Self::Weeks => "weeks",
Self::Months => "months",
Self::Years => "years",
}
}
}
impl fmt::Display for Resolution {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for Resolution {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::ALL.iter().copied().find(|r| r.as_str().eq_ignore_ascii_case(s)).ok_or_else(|| Error::Parse { kind: "resolution", input: s.to_owned() })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn names_round_trip() {
for &r in Resolution::ALL {
assert_eq!(r.to_string().parse::<Resolution>(), Ok(r));
assert_eq!(r.as_str().to_uppercase().parse::<Resolution>(), Ok(r));
}
assert!("fortnights".parse::<Resolution>().is_err());
}
#[test]
fn all_lists_every_resolution() {
let index = |r: Resolution| match r {
Resolution::Nanoseconds => 0,
Resolution::Microseconds => 1,
Resolution::Milliseconds => 2,
Resolution::Seconds => 3,
Resolution::Minutes => 4,
Resolution::Hours => 5,
Resolution::Days => 6,
Resolution::Weeks => 7,
Resolution::Months => 8,
Resolution::Years => 9,
};
assert_eq!(Resolution::ALL.len(), 10);
for (i, &r) in Resolution::ALL.iter().enumerate() {
assert_eq!(index(r), i);
}
}
#[test]
fn steps_increase() {
for pair in Resolution::ALL.windows(2) {
assert!(pair[0].step_nanos() < pair[1].step_nanos());
}
assert_eq!(Resolution::Months.step(), TimeDelta::days(30));
assert_eq!(Resolution::Years.step(), TimeDelta::days(365));
}
}