use core::fmt;
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Weekday {
#[cfg_attr(feature = "serde", serde(alias = "Monday"))]
Mon = 0,
#[cfg_attr(feature = "serde", serde(alias = "Tuesday"))]
Tue = 1,
#[cfg_attr(feature = "serde", serde(alias = "Wednesday"))]
Wed = 2,
#[cfg_attr(feature = "serde", serde(alias = "Thursday"))]
Thu = 3,
#[cfg_attr(feature = "serde", serde(alias = "Friday"))]
Fri = 4,
#[cfg_attr(feature = "serde", serde(alias = "Saturday"))]
Sat = 5,
#[cfg_attr(feature = "serde", serde(alias = "Sunday"))]
Sun = 6,
}
impl Weekday {
#[inline]
#[must_use]
pub const fn succ(&self) -> Self {
match *self {
Self::Mon => Self::Tue,
Self::Tue => Self::Wed,
Self::Wed => Self::Thu,
Self::Thu => Self::Fri,
Self::Fri => Self::Sat,
Self::Sat => Self::Sun,
Self::Sun => Self::Mon,
}
}
#[inline]
#[must_use]
pub const fn pred(&self) -> Self {
match *self {
Self::Mon => Self::Sun,
Self::Tue => Self::Mon,
Self::Wed => Self::Tue,
Self::Thu => Self::Wed,
Self::Fri => Self::Thu,
Self::Sat => Self::Fri,
Self::Sun => Self::Sat,
}
}
#[inline]
#[must_use]
pub const fn number_from_monday(&self) -> u8 {
self.days_since(Self::Mon) + 1
}
#[inline]
#[must_use]
pub const fn num_days_from_monday(&self) -> u8 {
self.days_since(Self::Mon)
}
#[must_use]
pub const fn days_since(&self, other: Self) -> u8 {
let lhs = *self as u8;
let rhs = other as u8;
if lhs < rhs { 7 + lhs - rhs } else { lhs - rhs }
}
}
impl fmt::Display for Weekday {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(match *self {
Self::Mon => "Mon",
Self::Tue => "Tue",
Self::Wed => "Wed",
Self::Thu => "Thu",
Self::Fri => "Fri",
Self::Sat => "Sat",
Self::Sun => "Sun",
})
}
}
impl From<u8> for Weekday {
fn from(value: u8) -> Self {
let value = value % 7; match value {
0 => Self::Mon,
1 => Self::Tue,
2 => Self::Wed,
3 => Self::Thu,
4 => Self::Fri,
5 => Self::Sat,
6 => Self::Sun,
_ => unreachable!(), }
}
}
impl num_traits::FromPrimitive for Weekday {
#[inline]
fn from_i64(n: i64) -> Option<Weekday> {
match n {
0 => Some(Self::Mon),
1 => Some(Self::Tue),
2 => Some(Self::Wed),
3 => Some(Self::Thu),
4 => Some(Self::Fri),
5 => Some(Self::Sat),
6 => Some(Self::Sun),
_ => None,
}
}
#[inline]
fn from_u64(n: u64) -> Option<Weekday> {
match n {
0 => Some(Self::Mon),
1 => Some(Self::Tue),
2 => Some(Self::Wed),
3 => Some(Self::Thu),
4 => Some(Self::Fri),
5 => Some(Self::Sat),
6 => Some(Self::Sun),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use num_traits::FromPrimitive;
use similar_asserts::assert_eq;
#[test]
fn test_days_since() {
for i in 0..7 {
let base_day = Weekday::from(i);
assert_eq!(
base_day.num_days_from_monday(),
base_day.days_since(Weekday::Mon)
);
assert_eq!(base_day.days_since(base_day), 0);
assert_eq!(base_day.days_since(base_day.pred()), 1);
assert_eq!(base_day.days_since(base_day.pred().pred()), 2);
assert_eq!(base_day.days_since(base_day.pred().pred().pred()), 3);
assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred()), 4);
assert_eq!(
base_day.days_since(base_day.pred().pred().pred().pred().pred()),
5
);
assert_eq!(
base_day.days_since(base_day.pred().pred().pred().pred().pred().pred()),
6
);
assert_eq!(base_day.days_since(base_day.succ()), 6);
assert_eq!(base_day.days_since(base_day.succ().succ()), 5);
assert_eq!(base_day.days_since(base_day.succ().succ().succ()), 4);
assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ()), 3);
assert_eq!(
base_day.days_since(base_day.succ().succ().succ().succ().succ()),
2
);
assert_eq!(
base_day.days_since(base_day.succ().succ().succ().succ().succ().succ()),
1
);
}
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_serialize() {
use Weekday::*;
use serde_json::to_string;
use std::vec;
let cases: vec::Vec<(Weekday, &str)> = vec![
(Mon, "\"Mon\""),
(Tue, "\"Tue\""),
(Wed, "\"Wed\""),
(Thu, "\"Thu\""),
(Fri, "\"Fri\""),
(Sat, "\"Sat\""),
(Sun, "\"Sun\""),
];
for (weekday, expected_str) in cases {
let string = to_string(&weekday).unwrap();
assert_eq!(string, expected_str);
}
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_deserialize() {
use Weekday::*;
use serde_json::from_str;
use std::vec;
let cases: vec::Vec<(&str, Weekday)> = vec![
("\"Mon\"", Mon),
("\"Monday\"", Mon),
("\"Tue\"", Tue),
("\"Tuesday\"", Tue),
("\"Wed\"", Wed),
("\"Wednesday\"", Wed),
("\"Thu\"", Thu),
("\"Thursday\"", Thu),
("\"Fri\"", Fri),
("\"Friday\"", Fri),
("\"Sat\"", Sat),
("\"Saturday\"", Sat),
("\"Sun\"", Sun),
("\"Sunday\"", Sun),
];
for (str, expected_weekday) in cases {
let weekday = from_str::<Weekday>(str).unwrap();
assert_eq!(weekday, expected_weekday);
}
let errors: vec::Vec<&str> = vec![
"\"not a weekday\"",
"\"monDAYs\"",
"\"mond\"",
"mon",
"\"thur\"",
"\"thurs\"",
];
for str in errors {
from_str::<Weekday>(str).unwrap_err();
}
}
#[test]
fn test_number_from_monday() {
for i in 0..7 {
let day = Weekday::from(i);
assert_eq!(day.number_from_monday(), i + 1);
assert_eq!(day.num_days_from_monday(), i);
}
}
#[test]
fn test_from_u64() {
assert_eq!(Weekday::from_u64(0), Some(Weekday::Mon));
assert_eq!(Weekday::from_u64(1), Some(Weekday::Tue));
assert_eq!(Weekday::from_u64(2), Some(Weekday::Wed));
assert_eq!(Weekday::from_u64(3), Some(Weekday::Thu));
assert_eq!(Weekday::from_u64(4), Some(Weekday::Fri));
assert_eq!(Weekday::from_u64(5), Some(Weekday::Sat));
assert_eq!(Weekday::from_u64(6), Some(Weekday::Sun));
assert_eq!(Weekday::from_u64(7), None);
}
#[test]
fn test_from_i64() {
assert_eq!(Weekday::from_i64(0), Some(Weekday::Mon));
assert_eq!(Weekday::from_i64(1), Some(Weekday::Tue));
assert_eq!(Weekday::from_i64(2), Some(Weekday::Wed));
assert_eq!(Weekday::from_i64(3), Some(Weekday::Thu));
assert_eq!(Weekday::from_i64(4), Some(Weekday::Fri));
assert_eq!(Weekday::from_i64(5), Some(Weekday::Sat));
assert_eq!(Weekday::from_i64(6), Some(Weekday::Sun));
assert_eq!(Weekday::from_i64(7), None);
}
}