use crate::session_date_calculator::{Day, NthWeekday}; use chrono::NaiveDate; use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct OldDatedSession { pub day: Day, pub session: Session, pub applying_exception: Option, } impl Noted for OldDatedSession { fn get_note(&self) -> Option<&String> { match &self.applying_exception { Some(e) => e.get_note(), None => self.session.get_note(), } } } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum DatedSession { Regular { session: RegularSession, day: Day, }, Extra(ExtraSession), Cancelled { session: RegularSession, day: Day, exception: CancelException, }, Altered { session: RegularSession, day: Day, exception: AlterException, }, } impl DatedSession { pub fn date(&self) -> &NaiveDate { match self { DatedSession::Regular { day, .. } => &day.date, DatedSession::Extra(ExtraSession { date, .. }) => date, DatedSession::Cancelled { day, .. } => &day.date, DatedSession::Altered { day, .. } => &day.date, } } } impl Noted for DatedSession { fn get_note(&self) -> Option<&String> { match self { DatedSession::Regular { session, .. } => session.get_note(), DatedSession::Extra(session) => session.get_note(), DatedSession::Cancelled { exception, .. } => exception.get_note(), DatedSession::Altered { exception, .. } => exception.get_note(), } } } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum Session { Regular(RegularSession), Extra(ExtraSession), } impl From for Session { fn from(value: RegularSession) -> Self { Self::Regular(value) } } impl From for Session { fn from(value: ExtraSession) -> Self { Self::Extra(value) } } impl Session { pub fn into_dated(self, day: Day) -> Result { if !self.is_applicable_to(&day) { return Err(self); } let dated_session = match self { Session::Regular(session) => match session.except.get(&day.date) { Some(exception) => match exception.clone() { Exception::Cancel(exception) => DatedSession::Cancelled { session, day, exception, }, Exception::Alter(exception) => DatedSession::Altered { session, day, exception, }, }, None => DatedSession::Regular { session, day }, }, Session::Extra(session) => DatedSession::Extra(session), }; Ok(dated_session) } pub fn is_applicable_to(&self, day: &Day) -> bool { match *self { Session::Regular(RegularSession { rule, .. }) => rule.matches(day), Session::Extra(ExtraSession { date, .. }) => day.date == date, } } } impl Noted for Session { fn get_note(&self) -> Option<&String> { match self { Session::Regular(session) => session.get_note(), Session::Extra(session) => session.get_note(), } } } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct RegularSession { pub rule: NthWeekday, pub note: Option, pub except: HashMap, } impl From for RegularSession { fn from(rule: NthWeekday) -> Self { Self { rule, note: None, except: Default::default(), } } } impl RegularSession { pub fn with_exception(mut self, date: D, exception: E) -> Self where D: Into, E: Into, { self.except.insert(date.into(), exception.into()); self } } #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct ExtraSession { pub date: NaiveDate, pub note: Option, } impl From for ExtraSession { fn from(date: NaiveDate) -> Self { Self { date, note: None } } } #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum Exception { Cancel(CancelException), Alter(AlterException), } impl From for Exception { fn from(value: CancelException) -> Self { Self::Cancel(value) } } impl From for Exception { fn from(value: AlterException) -> Self { Self::Alter(value) } } impl Noted for Exception { fn get_note(&self) -> Option<&String> { match self { Exception::Cancel(exception) => exception.get_note(), Exception::Alter(exception) => exception.get_note(), } } } #[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)] #[serde(default)] pub struct CancelException { pub note: Option, } #[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)] #[serde(default)] pub struct AlterException { pub date: Option, pub note: Option, } impl From for AlterException { fn from(value: NaiveDate) -> Self { Self { date: Some(value), ..Self::default() } } } pub trait Noted { fn get_note(&self) -> Option<&String>; } macro_rules! impl_noted { ($ty: ident) => { impl Noted for $ty { fn get_note(&self) -> Option<&String> { self.note.as_ref() } } }; } impl_noted!(RegularSession); impl_noted!(ExtraSession); impl_noted!(AlterException); impl_noted!(CancelException); pub trait WithNote { fn with_note(self, note: S) -> Self where S: ToString; } macro_rules! impl_with_node { ($ty: ident) => { impl WithNote for $ty { fn with_note(self, note: S) -> Self where S: ToString, { #[allow(clippy::needless_update)] Self { note: Some(note.to_string()), ..self } } } }; } impl_with_node!(AlterException); impl_with_node!(CancelException); impl_with_node!(ExtraSession); impl_with_node!(RegularSession);