summaryrefslogtreecommitdiff
path: root/src/weekday.rs
blob: 5679bc787f984491eefafca820c7181dd2155286 (plain)
use std::str::FromStr;

use derive_more::Display;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use thiserror::Error;

use self::Weekday::*;

/// Day of the week
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum Weekday {
	Monday = 0,
	Tuesday = 1,
	Wednesday = 2,
	Thursday = 3,
	Friday = 4,
	Saturday = 5,
	Sunday = 6,
}

impl Weekday {
	/// Get the weekday from its name. Returns `None` if an invalid name was given.
	///
	/// # Example
	///
	/// ```
	/// use botic::Weekday;
	///
	/// assert_eq!(Weekday::Monday, Weekday::from_name("Monday").unwrap());
	/// assert_eq!(None, Weekday::from_name("monday"));
	/// ```
	pub fn from_name(name: &str) -> Option<Self> {
		match name {
			"Monday" => Some(Monday),
			"Tuesday" => Some(Tuesday),
			"Wednesday" => Some(Wednesday),
			"Thursday" => Some(Thursday),
			"Friday" => Some(Friday),
			"Saturday" => Some(Saturday),
			"Sunday" => Some(Sunday),
			_ => None,
		}
	}

	/// Get the next weekday
	///
	/// # Example
	///
	/// ```
	/// use botic::Weekday;
	///
	/// assert_eq!(Weekday::Tuesday, Weekday::Monday.next());
	/// ```
	pub const fn next(self) -> Self {
		match self {
			Monday => Tuesday,
			Tuesday => Wednesday,
			Wednesday => Thursday,
			Thursday => Friday,
			Friday => Saturday,
			Saturday => Sunday,
			Sunday => Monday,
		}
	}

	/// Get the previous weekday
	///
	/// # Example
	///
	/// ```
	/// use botic::Weekday;
	///
	/// assert_eq!(Weekday::Sunday, Weekday::Monday.previous());
	/// ```
	pub const fn previous(self) -> Self {
		match self {
			Monday => Sunday,
			Tuesday => Monday,
			Wednesday => Tuesday,
			Thursday => Wednesday,
			Friday => Thursday,
			Saturday => Friday,
			Sunday => Saturday,
		}
	}

	/// Get the zero-indexed number of days from Monday.
	/// In other words, the number representing the day of the week,
	/// starting with Monday = 0
	///
	/// # Example
	///
	/// ```
	/// use botic::Weekday;
	///
	/// assert_eq!(0, Weekday::Monday.number_days_from_monday());
	/// assert_eq!(6, Weekday::Sunday.number_days_from_monday());
	/// ```
	pub const fn number_days_from_monday(self) -> u8 {
		self as u8
	}

	/// Get the one-indexed number of days from Monday.
	/// In other words, the number representing the day of the week,
	/// starting with Monday = 1
	///
	/// # Example
	///
	/// ```
	/// use botic::Weekday;
	///
	/// assert_eq!(1, Weekday::Monday.number_from_monday());
	/// assert_eq!(7, Weekday::Sunday.number_from_monday());
	/// ```
	pub const fn number_from_monday(self) -> u8 {
		self.number_days_from_monday() + 1
	}

	/// Get the zero-indexed number of days from Sunday.
	/// In other words, the number representing the day of the week,
	/// starting with Sunday = 0
	///
	/// # Example
	///
	/// ```
	/// use botic::Weekday;
	///
	/// assert_eq!(0, Weekday::Sunday.number_days_from_sunday());
	/// assert_eq!(1, Weekday::Monday.number_days_from_sunday());
	/// ```
	// TODO benchmark this
	pub const fn number_days_from_sunday(self) -> u8 {
		match self {
			Sunday => 0,
			_ => (self as u8) + 1,
		}
	}

	/// Get the one-indexed number of days from Sunday.
	/// In other words, the number representing the day of the week,
	/// starting with Sunday = 1
	///
	/// # Example
	///
	/// ```
	/// use botic::Weekday;
	///
	/// assert_eq!(1, Weekday::Sunday.number_from_sunday());
	/// assert_eq!(2, Weekday::Monday.number_from_sunday());
	/// ```
	pub const fn number_from_sunday(self) -> u8 {
		self.number_days_from_sunday() + 1
	}
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Error)]
#[error("Failed to parse the month")]
// TODO Consider trying to figure out what month the user meant to use
pub struct ParseWeekdayError;

// TODO make case-insensitive
// TODO support short names
impl FromStr for Weekday {
	type Err = ParseWeekdayError;

	fn from_str(s: &str) -> Result<Self, Self::Err> {
		match Self::from_name(s) {
			Some(weekday) => Ok(weekday),
			None => Err(ParseWeekdayError),
		}
	}
}