From bc3c4dedf9b8ca66263f96811c84eefad42a2340 Mon Sep 17 00:00:00 2001 From: Steppy Date: Mon, 17 Feb 2025 22:40:49 +0100 Subject: [PATCH] Implement Iterator for SessionDayIter --- session_iter/src/session.rs | 8 ++- session_iter/src/session/iter.rs | 120 ++++++++++++++++++++++++------- session_iter/src/session/rule.rs | 25 ++++--- 3 files changed, 112 insertions(+), 41 deletions(-) diff --git a/session_iter/src/session.rs b/session_iter/src/session.rs index 51134a4..d927cfb 100644 --- a/session_iter/src/session.rs +++ b/session_iter/src/session.rs @@ -2,7 +2,7 @@ pub mod iter; pub mod rule; use crate::day::Day; -use crate::session::rule::{SessionRule, SessionRuleLike, WeekdayOfMonth}; +use crate::session::rule::{SessionRule, WeekdayOfMonth}; use chrono::NaiveDate; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -98,6 +98,7 @@ impl RegularSession { 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, @@ -148,13 +149,14 @@ impl_opt_noted!(Except with Alternation, Cancellation); #[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)] pub struct Alternation { pub note: Note, - pub day: Option, + ///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 { - day: Some(day), + new_day: Some(day), ..Default::default() } } diff --git a/session_iter/src/session/iter.rs b/session_iter/src/session/iter.rs index addcaef..098b7ac 100644 --- a/session_iter/src/session/iter.rs +++ b/session_iter/src/session/iter.rs @@ -2,14 +2,14 @@ use crate::day::Day; use crate::session::{ Alternation, Cancellation, Dated, Except, ExtraSession, OptNoted, RegularSession, Session, }; +use chrono::Days; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, VecDeque}; use std::hash::Hash; #[derive(Debug, Clone)] pub struct DatedSessionIter { - current_date: Day, - sessions: BTreeMap>, + sessions: BTreeMap>, } impl DatedSessionIter { @@ -20,7 +20,7 @@ impl DatedSessionIter { { let current_date = start_date.into(); - //map every session and their exceptions to a date when they should show up in the calendar + //map every session and their excepts to a date when they should show up in the calendar let sessions = sessions .into_iter() @@ -28,10 +28,9 @@ impl DatedSessionIter { Session::Regular(session) => session .except .iter() - .filter(|(&day, _)| current_date <= day) .map(|(&day, except)| match except { Except::Alternation(alternation) => DatedSession::Altered { - day: alternation.day.unwrap_or(day), + day, regular: session.clone(), cause: alternation.clone(), }, @@ -48,31 +47,55 @@ impl DatedSessionIter { } })) .collect(), - Session::Extra(extra) => { - //filter out old extra sessions - if current_date <= extra.day() { - vec![extra.into()] - } else { - vec![] - } - } + Session::Extra(extra) => vec![extra.into()], }) - .fold(BTreeMap::<_, Vec<_>>::new(), |mut map, dated_session| { - map - .entry(dated_session.day()) - .or_default() - .push(dated_session); - map - }); + //filter out and entries which would lay in the past + .filter(|dated_session| dated_session.day() >= current_date) + //group sessions on the same day together + .fold( + BTreeMap::<_, VecDeque<_>>::new(), + |mut map, dated_session| { + map + .entry(dated_session.day()) + .or_default() + .push_back(dated_session); + map + }, + ); - Self { - current_date, - sessions, - } + Self { sessions } } } -//TODO iter implementation for DatedSessionIter +impl Iterator for DatedSessionIter { + type Item = DatedSession; + + fn next(&mut self) -> Option { + let mut entry = self.sessions.first_entry()?; + let session = entry.get_mut().pop_front()?; + if entry.get().is_empty() { + entry.remove(); + } + + //make sure regular sessions remain in map + if let DatedSession::Regular { ref session, day } = session { + //calculate next day, because next_regular_session_day of a session day returns that very session day + if let Some(next_day) = day.checked_add_days(Days::new(1)) { + if let Some(next_session_day) = session.next_regular_session_day(next_day) { + self + .sessions + .entry(next_session_day) + .or_default() + .push_back(DatedSession::Regular { + day: next_session_day, + session: session.clone(), + }) + } + } + } + Some(session) + } +} #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum DatedSession { @@ -106,7 +129,7 @@ impl Dated for DatedSession { DatedSession::Regular { day, .. } => day, DatedSession::Extra(ref extra) => extra.day(), DatedSession::Canceled { day, .. } => day, - DatedSession::Altered { day, ref cause, .. } => cause.day.unwrap_or(day), + DatedSession::Altered { day, ref cause, .. } => cause.new_day.unwrap_or(day), } } } @@ -121,3 +144,46 @@ impl OptNoted for DatedSession { } } } + +#[cfg(test)] +mod test { + use crate::session::iter::{DatedSession, DatedSessionIter}; + use crate::session::rule::WeekdayOfMonth; + use crate::session::{RegularSession, WithNote}; + use crate::test_util::{date, day}; + use chrono::Weekday; + + #[test] + fn test_regular() { + let tue_session = + RegularSession::from(WeekdayOfMonth::new(3, Weekday::Tue)).with_note("18:30 Uhr"); + let sun_session = + RegularSession::from(WeekdayOfMonth::new(1, Weekday::Sun)).with_note("10:00 Uhr"); + let mut iter = DatedSessionIter::new( + date(17, 2, 2025), + [tue_session.clone().into(), sun_session.clone().into()], + ); + + assert_eq!( + iter.next(), + Some(DatedSession::Regular { + day: day(18, 2, 2025), + session: tue_session.clone() + }) + ); + assert_eq!( + iter.next(), + Some(DatedSession::Regular { + day: day(2, 3, 2025), + session: sun_session.clone() + }) + ); + assert_eq!( + iter.next(), + Some(DatedSession::Regular { + day: day(18, 3, 2025), + session: tue_session.clone() + }) + ); + } +} diff --git a/session_iter/src/session/rule.rs b/session_iter/src/session/rule.rs index 8ac0490..b7793b2 100644 --- a/session_iter/src/session/rule.rs +++ b/session_iter/src/session/rule.rs @@ -1,5 +1,5 @@ use crate::day::Day; -use chrono::{Datelike, Months, NaiveDate, Weekday}; +use chrono::{Datelike, Days, Months, NaiveDate, Weekday}; use serde::{Deserialize, Serialize}; use std::ops::Deref; @@ -21,7 +21,7 @@ impl SessionRule { { SessionDayIter { rule: self, - current_date: Some(start_date.into()), + start_date: Some(start_date.into()), } } } @@ -45,20 +45,23 @@ impl SessionRuleLike for SessionRule { #[derive(Debug, Clone)] pub struct SessionDayIter { pub rule: R, - pub current_date: Option, + pub start_date: Option, } -impl Iterator for SessionDayIter +impl Iterator for SessionDayIter where - R: Deref, + R: Deref, + S: SessionRuleLike, { type Item = Day; fn next(&mut self) -> Option { - self.current_date = self - .current_date - .and_then(|date| self.rule.determine_next_date(date)); - self.current_date + let start_date = self.start_date?; + let session_date = self.rule.determine_next_date(start_date); + self.start_date = session_date + .and_then(|session_date| session_date.checked_add_days(Days::new(1))) + .map(Day::from); + session_date } } @@ -69,7 +72,7 @@ where pub fn to_owned(&self) -> SessionDayIter { SessionDayIter { rule: self.rule.clone(), - current_date: self.current_date, + start_date: self.start_date, } } } @@ -116,7 +119,7 @@ impl SessionRuleLike for WeekdayOfMonth { } pub trait SessionRuleLike { - /// Determines the next session date in form of a [Day], possibly including the `start_date`. + /// Determines the next session date in form of a [Day], possibly including the `current_date`. fn determine_next_date(&self, current_date: Day) -> Option; /// Whether this rule would be able to produce the given `day`.