use crate::{
error::Error,
util::{
rangeint::{RFrom, RInto},
t::{self, C},
},
};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(u8)]
#[allow(missing_docs)]
pub enum Weekday {
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
Sunday = 7,
}
impl Weekday {
#[inline]
pub fn from_monday_zero_offset(offset: i8) -> Result<Weekday, Error> {
let offset = t::WeekdayZero::try_new("weekday", offset)?;
Ok(Weekday::from_monday_zero_offset_ranged(offset))
}
#[inline]
pub fn from_monday_one_offset(offset: i8) -> Result<Weekday, Error> {
let offset = t::WeekdayOne::try_new("weekday", offset)?;
Ok(Weekday::from_monday_one_offset_ranged(offset))
}
#[inline]
pub fn from_sunday_zero_offset(offset: i8) -> Result<Weekday, Error> {
let offset = t::WeekdayZero::try_new("weekday", offset)?;
Ok(Weekday::from_sunday_zero_offset_ranged(offset))
}
#[inline]
pub fn from_sunday_one_offset(offset: i8) -> Result<Weekday, Error> {
let offset = t::WeekdayOne::try_new("weekday", offset)?;
Ok(Weekday::from_sunday_one_offset_ranged(offset))
}
#[inline]
pub fn to_monday_zero_offset(self) -> i8 {
self.to_monday_zero_offset_ranged().get()
}
#[inline]
pub fn to_monday_one_offset(self) -> i8 {
self.to_monday_one_offset_ranged().get()
}
#[inline]
pub fn to_sunday_zero_offset(self) -> i8 {
self.to_sunday_zero_offset_ranged().get()
}
#[inline]
pub fn to_sunday_one_offset(self) -> i8 {
self.to_sunday_one_offset_ranged().get()
}
#[inline]
pub fn next(self) -> Weekday {
self.wrapping_add(1)
}
#[inline]
pub fn previous(self) -> Weekday {
self.wrapping_sub(1)
}
#[inline]
pub fn since(self, other: Weekday) -> i8 {
self.since_ranged(other).get()
}
#[inline]
pub fn until(self, other: Weekday) -> i8 {
self.until_ranged(other).get()
}
#[inline]
pub fn wrapping_add<D: Into<i64>>(self, days: D) -> Weekday {
let start = t::NoUnits::rfrom(self.to_monday_zero_offset_ranged());
let rhs = t::NoUnits::new(days.into()).unwrap();
let end = start.wrapping_add(rhs) % C(7);
Weekday::from_monday_zero_offset_ranged(end)
}
#[inline]
pub fn wrapping_sub<D: Into<i64>>(self, days: D) -> Weekday {
self.wrapping_add(-days.into())
}
#[inline]
pub fn cycle_forward(self) -> WeekdaysForward {
let nexts = [
self,
self.wrapping_add(1),
self.wrapping_add(2),
self.wrapping_add(3),
self.wrapping_add(4),
self.wrapping_add(5),
self.wrapping_add(6),
];
WeekdaysForward { it: nexts.into_iter().cycle() }
}
#[inline]
pub fn cycle_reverse(self) -> WeekdaysReverse {
let nexts = [
self,
self.wrapping_sub(1),
self.wrapping_sub(2),
self.wrapping_sub(3),
self.wrapping_sub(4),
self.wrapping_sub(5),
self.wrapping_sub(6),
];
WeekdaysReverse { it: nexts.into_iter().cycle() }
}
}
impl Weekday {
#[inline]
pub(crate) fn from_monday_zero_offset_ranged(
offset: impl RInto<t::WeekdayZero>,
) -> Weekday {
match offset.rinto().get() {
0 => Weekday::Monday,
1 => Weekday::Tuesday,
2 => Weekday::Wednesday,
3 => Weekday::Thursday,
4 => Weekday::Friday,
5 => Weekday::Saturday,
6 => Weekday::Sunday,
_ => unreachable!(),
}
}
#[inline]
pub(crate) fn from_monday_one_offset_ranged(
offset: impl RInto<t::WeekdayOne>,
) -> Weekday {
let offset_zero = offset.rinto() - C(1);
Weekday::from_monday_zero_offset_ranged(offset_zero)
}
#[inline]
pub(crate) fn from_sunday_zero_offset_ranged(
offset: impl RInto<t::WeekdayZero>,
) -> Weekday {
let offset_sunday = (offset.rinto() - C(1)) % C(7);
Weekday::from_monday_zero_offset_ranged(offset_sunday)
}
#[inline]
pub(crate) fn from_sunday_one_offset_ranged(
offset: impl RInto<t::WeekdayOne>,
) -> Weekday {
let offset_zero = offset.rinto() - C(1);
Weekday::from_sunday_zero_offset_ranged(offset_zero)
}
#[inline]
pub(crate) fn to_monday_zero_offset_ranged(self) -> t::WeekdayZero {
(self.to_monday_one_offset_ranged() - C(1)).rinto()
}
#[inline]
pub(crate) fn to_monday_one_offset_ranged(self) -> t::WeekdayOne {
t::WeekdayOne::new_unchecked(self as i8)
}
#[inline]
pub(crate) fn to_sunday_zero_offset_ranged(self) -> t::WeekdayZero {
(self.to_monday_zero_offset_ranged() + C(1)) % C(7)
}
#[inline]
pub(crate) fn to_sunday_one_offset_ranged(self) -> t::WeekdayOne {
(self.to_sunday_zero_offset_ranged() + C(1)).rinto()
}
#[inline]
pub(crate) fn since_ranged(self, other: Weekday) -> t::WeekdayZero {
(self.to_monday_zero_offset_ranged()
- other.to_monday_zero_offset_ranged())
% C(7)
}
#[inline]
pub(crate) fn until_ranged(self, other: Weekday) -> t::WeekdayZero {
other.since_ranged(self)
}
}
impl core::ops::Add<i8> for Weekday {
type Output = Weekday;
#[inline]
fn add(self, rhs: i8) -> Weekday {
self.wrapping_add(rhs)
}
}
impl core::ops::Add<i16> for Weekday {
type Output = Weekday;
#[inline]
fn add(self, rhs: i16) -> Weekday {
self.wrapping_add(rhs)
}
}
impl core::ops::Add<i32> for Weekday {
type Output = Weekday;
#[inline]
fn add(self, rhs: i32) -> Weekday {
self.wrapping_add(rhs)
}
}
impl core::ops::Add<i64> for Weekday {
type Output = Weekday;
#[inline]
fn add(self, rhs: i64) -> Weekday {
self.wrapping_add(rhs)
}
}
impl core::ops::Add<Weekday> for i8 {
type Output = Weekday;
#[inline]
fn add(self, rhs: Weekday) -> Weekday {
rhs.wrapping_add(self)
}
}
impl core::ops::Add<Weekday> for i16 {
type Output = Weekday;
#[inline]
fn add(self, rhs: Weekday) -> Weekday {
rhs.wrapping_add(self)
}
}
impl core::ops::Add<Weekday> for i32 {
type Output = Weekday;
#[inline]
fn add(self, rhs: Weekday) -> Weekday {
rhs.wrapping_add(self)
}
}
impl core::ops::Add<Weekday> for i64 {
type Output = Weekday;
#[inline]
fn add(self, rhs: Weekday) -> Weekday {
rhs.wrapping_add(self)
}
}
impl core::ops::AddAssign<i8> for Weekday {
#[inline]
fn add_assign(&mut self, rhs: i8) {
*self = *self + rhs;
}
}
impl core::ops::AddAssign<i16> for Weekday {
#[inline]
fn add_assign(&mut self, rhs: i16) {
*self = *self + rhs;
}
}
impl core::ops::AddAssign<i32> for Weekday {
#[inline]
fn add_assign(&mut self, rhs: i32) {
*self = *self + rhs;
}
}
impl core::ops::AddAssign<i64> for Weekday {
#[inline]
fn add_assign(&mut self, rhs: i64) {
*self = *self + rhs;
}
}
impl core::ops::Sub<i8> for Weekday {
type Output = Weekday;
#[inline]
fn sub(self, rhs: i8) -> Weekday {
self.wrapping_sub(rhs)
}
}
impl core::ops::Sub<i16> for Weekday {
type Output = Weekday;
#[inline]
fn sub(self, rhs: i16) -> Weekday {
self.wrapping_sub(rhs)
}
}
impl core::ops::Sub<i32> for Weekday {
type Output = Weekday;
#[inline]
fn sub(self, rhs: i32) -> Weekday {
self.wrapping_sub(rhs)
}
}
impl core::ops::Sub<i64> for Weekday {
type Output = Weekday;
#[inline]
fn sub(self, rhs: i64) -> Weekday {
self.wrapping_sub(rhs)
}
}
impl core::ops::SubAssign<i8> for Weekday {
#[inline]
fn sub_assign(&mut self, rhs: i8) {
*self = *self - rhs;
}
}
impl core::ops::SubAssign<i16> for Weekday {
#[inline]
fn sub_assign(&mut self, rhs: i16) {
*self = *self - rhs;
}
}
impl core::ops::SubAssign<i32> for Weekday {
#[inline]
fn sub_assign(&mut self, rhs: i32) {
*self = *self - rhs;
}
}
impl core::ops::SubAssign<i64> for Weekday {
#[inline]
fn sub_assign(&mut self, rhs: i64) {
*self = *self - rhs;
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for Weekday {
fn arbitrary(g: &mut quickcheck::Gen) -> Weekday {
let offset = t::WeekdayZero::arbitrary(g);
Weekday::from_monday_zero_offset_ranged(offset)
}
fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Weekday>> {
alloc::boxed::Box::new(
self.to_monday_zero_offset_ranged()
.shrink()
.map(Weekday::from_monday_zero_offset_ranged),
)
}
}
#[derive(Clone, Debug)]
pub struct WeekdaysForward {
it: core::iter::Cycle<core::array::IntoIter<Weekday, 7>>,
}
impl Iterator for WeekdaysForward {
type Item = Weekday;
#[inline]
fn next(&mut self) -> Option<Weekday> {
self.it.next()
}
}
impl core::iter::FusedIterator for WeekdaysForward {}
#[derive(Clone, Debug)]
pub struct WeekdaysReverse {
it: core::iter::Cycle<core::array::IntoIter<Weekday, 7>>,
}
impl Iterator for WeekdaysReverse {
type Item = Weekday;
#[inline]
fn next(&mut self) -> Option<Weekday> {
self.it.next()
}
}
impl core::iter::FusedIterator for WeekdaysReverse {}
#[cfg(test)]
mod tests {
use super::*;
quickcheck::quickcheck! {
fn prop_since_add_equals_self(wd1: Weekday, wd2: Weekday) -> bool {
let days = wd1.since(wd2);
wd2.wrapping_add(days) == wd1
}
fn prop_until_add_equals_other(wd1: Weekday, wd2: Weekday) -> bool {
let days = wd1.until(wd2);
wd1.wrapping_add(days) == wd2
}
}
}