Implement Iterator for SessionDayIter
This commit is contained in:
parent
4fd43332e0
commit
bc3c4dedf9
@ -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<D>(&self, current_date: D) -> Option<Day>
|
||||
where
|
||||
D: Into<Day>,
|
||||
@ -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<Day>,
|
||||
///the date when the alternation should show up in the calendar, or the original date
|
||||
pub new_day: Option<Day>,
|
||||
}
|
||||
|
||||
impl From<Day> for Alternation {
|
||||
fn from(day: Day) -> Self {
|
||||
Self {
|
||||
day: Some(day),
|
||||
new_day: Some(day),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Day, Vec<DatedSession>>,
|
||||
sessions: BTreeMap<Day, VecDeque<DatedSession>>,
|
||||
}
|
||||
|
||||
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<Self::Item> {
|
||||
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()
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<R> {
|
||||
pub rule: R,
|
||||
pub current_date: Option<Day>,
|
||||
pub start_date: Option<Day>,
|
||||
}
|
||||
|
||||
impl<R> Iterator for SessionDayIter<R>
|
||||
impl<R, S> Iterator for SessionDayIter<R>
|
||||
where
|
||||
R: Deref<Target = SessionRule>,
|
||||
R: Deref<Target = S>,
|
||||
S: SessionRuleLike,
|
||||
{
|
||||
type Item = Day;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<SessionRule> {
|
||||
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<Day>;
|
||||
|
||||
/// Whether this rule would be able to produce the given `day`.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user