pub mod iter; pub mod rule; use crate::day::Day; use crate::session::rule::{SessionRule, WeekdayOfMonth}; use chrono::NaiveDate; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; type Note = Option; macro_rules! impl_opt_noted { ($ty: ident) => { impl OptNoted for $ty { fn note(&self) -> Option<&str> { self.note.as_ref().map(String::as_str) } } }; ($ty: ident with $($variant: ident),+) => { impl OptNoted for $ty { fn note(&self) -> Option<&str> { match self { $(Self::$variant(opt_noted) => opt_noted.note(),)+ } } } }; } macro_rules! impl_with_note { ($ty: ident) => { impl WithNote for $ty { fn with_note(self, note: &str) -> Self { #[allow(clippy::needless_update)] Self { note: Some(note.to_owned()), ..self } } } }; } macro_rules! impl_from { ($what: ident for $ty: ident by $intermediate: ident) => { impl From<$what> for $ty { fn from(value: $what) -> Self { Self::from($intermediate::from(value)) } } }; ($what: ident for $ty: ident as $variant: ident) => { impl From<$what> for $ty { fn from(value: $what) -> Self { Self::$variant(value) } } }; ($what: ident for $ty: ident) => { impl_from!($what for $ty as $what); }; } #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum Session { Regular(RegularSession), Extra(ExtraSession), } impl_from!(RegularSession for Session as Regular); impl_from!(ExtraSession for Session as Extra); #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct RegularSession { pub rule: SessionRule, pub note: Note, #[serde(with = "serde_json_any_key::any_key_map")] pub except: BTreeMap, } impl From for RegularSession { fn from(rule: SessionRule) -> Self { Self { rule, note: None, except: Default::default(), } } } impl RegularSession { pub fn except(mut self, day: D, except: E) -> Self where D: Into, E: Into, { self.except.insert(day.into(), except.into()); self } ///gets the next session day, where no except applies. Can possibly return the `current_date`. pub fn next_regular_session_day(&self, current_date: D) -> Option where D: Into, { self .rule .to_session_day_iter(current_date) .find(|day| !self.except.contains_key(day)) } } impl_from!(WeekdayOfMonth for RegularSession by SessionRule); impl_opt_noted!(RegularSession); impl_with_note!(RegularSession); #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub struct ExtraSession { pub day: Day, pub note: Note, } impl From for ExtraSession { fn from(day: Day) -> Self { Self { day, note: None } } } impl_from!(NaiveDate for ExtraSession by Day); impl_opt_noted!(ExtraSession); impl_with_note!(ExtraSession); impl Dated for ExtraSession { fn day(&self) -> Day { self.day } } #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum Except { Alternation(Alternation), Cancellation(Cancellation), } impl_from!(Alternation for Except); impl_from!(Cancellation for Except); impl_opt_noted!(Except with Alternation, Cancellation); #[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)] pub struct Alternation { pub note: Note, ///the date when the alternation should show up in the calendar, or the original date pub new_day: Option, } impl From for Alternation { fn from(day: Day) -> Self { Self { new_day: Some(day), ..Default::default() } } } impl_from!(NaiveDate for Alternation by Day); impl_opt_noted!(Alternation); impl_with_note!(Alternation); #[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)] #[serde(default)] pub struct Cancellation { pub note: Note, } impl Cancellation { pub fn new() -> Self { Self::default() } } impl_opt_noted!(Cancellation); impl_with_note!(Cancellation); pub trait Dated { /// The day when this should show up in a calendar fn day(&self) -> Day; } pub trait OptNoted { fn note(&self) -> Option<&str>; } pub trait WithNote { fn with_note(self, note: &str) -> Self; }