Implement Iterator for SessionDayIter
This commit is contained in:
parent
4fd43332e0
commit
bc3c4dedf9
@ -2,7 +2,7 @@ pub mod iter;
|
|||||||
pub mod rule;
|
pub mod rule;
|
||||||
|
|
||||||
use crate::day::Day;
|
use crate::day::Day;
|
||||||
use crate::session::rule::{SessionRule, SessionRuleLike, WeekdayOfMonth};
|
use crate::session::rule::{SessionRule, WeekdayOfMonth};
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@ -98,6 +98,7 @@ impl RegularSession {
|
|||||||
self
|
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>
|
pub fn next_regular_session_day<D>(&self, current_date: D) -> Option<Day>
|
||||||
where
|
where
|
||||||
D: Into<Day>,
|
D: Into<Day>,
|
||||||
@ -148,13 +149,14 @@ impl_opt_noted!(Except with Alternation, Cancellation);
|
|||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
|
||||||
pub struct Alternation {
|
pub struct Alternation {
|
||||||
pub note: Note,
|
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 {
|
impl From<Day> for Alternation {
|
||||||
fn from(day: Day) -> Self {
|
fn from(day: Day) -> Self {
|
||||||
Self {
|
Self {
|
||||||
day: Some(day),
|
new_day: Some(day),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,14 +2,14 @@ use crate::day::Day;
|
|||||||
use crate::session::{
|
use crate::session::{
|
||||||
Alternation, Cancellation, Dated, Except, ExtraSession, OptNoted, RegularSession, Session,
|
Alternation, Cancellation, Dated, Except, ExtraSession, OptNoted, RegularSession, Session,
|
||||||
};
|
};
|
||||||
|
use chrono::Days;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, VecDeque};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DatedSessionIter {
|
pub struct DatedSessionIter {
|
||||||
current_date: Day,
|
sessions: BTreeMap<Day, VecDeque<DatedSession>>,
|
||||||
sessions: BTreeMap<Day, Vec<DatedSession>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DatedSessionIter {
|
impl DatedSessionIter {
|
||||||
@ -20,7 +20,7 @@ impl DatedSessionIter {
|
|||||||
{
|
{
|
||||||
let current_date = start_date.into();
|
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 =
|
let sessions =
|
||||||
sessions
|
sessions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -28,10 +28,9 @@ impl DatedSessionIter {
|
|||||||
Session::Regular(session) => session
|
Session::Regular(session) => session
|
||||||
.except
|
.except
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(&day, _)| current_date <= day)
|
|
||||||
.map(|(&day, except)| match except {
|
.map(|(&day, except)| match except {
|
||||||
Except::Alternation(alternation) => DatedSession::Altered {
|
Except::Alternation(alternation) => DatedSession::Altered {
|
||||||
day: alternation.day.unwrap_or(day),
|
day,
|
||||||
regular: session.clone(),
|
regular: session.clone(),
|
||||||
cause: alternation.clone(),
|
cause: alternation.clone(),
|
||||||
},
|
},
|
||||||
@ -48,31 +47,55 @@ impl DatedSessionIter {
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.collect(),
|
.collect(),
|
||||||
Session::Extra(extra) => {
|
Session::Extra(extra) => vec![extra.into()],
|
||||||
//filter out old extra sessions
|
|
||||||
if current_date <= extra.day() {
|
|
||||||
vec![extra.into()]
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.fold(BTreeMap::<_, Vec<_>>::new(), |mut map, dated_session| {
|
//filter out and entries which would lay in the past
|
||||||
map
|
.filter(|dated_session| dated_session.day() >= current_date)
|
||||||
.entry(dated_session.day())
|
//group sessions on the same day together
|
||||||
.or_default()
|
.fold(
|
||||||
.push(dated_session);
|
BTreeMap::<_, VecDeque<_>>::new(),
|
||||||
map
|
|mut map, dated_session| {
|
||||||
});
|
map
|
||||||
|
.entry(dated_session.day())
|
||||||
|
.or_default()
|
||||||
|
.push_back(dated_session);
|
||||||
|
map
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
Self {
|
Self { sessions }
|
||||||
current_date,
|
|
||||||
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)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
pub enum DatedSession {
|
pub enum DatedSession {
|
||||||
@ -106,7 +129,7 @@ impl Dated for DatedSession {
|
|||||||
DatedSession::Regular { day, .. } => day,
|
DatedSession::Regular { day, .. } => day,
|
||||||
DatedSession::Extra(ref extra) => extra.day(),
|
DatedSession::Extra(ref extra) => extra.day(),
|
||||||
DatedSession::Canceled { day, .. } => 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 crate::day::Day;
|
||||||
use chrono::{Datelike, Months, NaiveDate, Weekday};
|
use chrono::{Datelike, Days, Months, NaiveDate, Weekday};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ impl SessionRule {
|
|||||||
{
|
{
|
||||||
SessionDayIter {
|
SessionDayIter {
|
||||||
rule: self,
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SessionDayIter<R> {
|
pub struct SessionDayIter<R> {
|
||||||
pub rule: 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
|
where
|
||||||
R: Deref<Target = SessionRule>,
|
R: Deref<Target = S>,
|
||||||
|
S: SessionRuleLike,
|
||||||
{
|
{
|
||||||
type Item = Day;
|
type Item = Day;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
self.current_date = self
|
let start_date = self.start_date?;
|
||||||
.current_date
|
let session_date = self.rule.determine_next_date(start_date);
|
||||||
.and_then(|date| self.rule.determine_next_date(date));
|
self.start_date = session_date
|
||||||
self.current_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> {
|
pub fn to_owned(&self) -> SessionDayIter<SessionRule> {
|
||||||
SessionDayIter {
|
SessionDayIter {
|
||||||
rule: self.rule.clone(),
|
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 {
|
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>;
|
fn determine_next_date(&self, current_date: Day) -> Option<Day>;
|
||||||
|
|
||||||
/// Whether this rule would be able to produce the given `day`.
|
/// Whether this rule would be able to produce the given `day`.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user