diff options
author | Fredrik Meringdal <fredrikmeringdal@Fredriks-MacBook-Pro.local> | 2021-02-03 20:36:10 +0100 |
---|---|---|
committer | Fredrik Meringdal <fredrikmeringdal@Fredriks-MacBook-Pro.local> | 2021-02-03 20:36:10 +0100 |
commit | 11f9f1a7b2339898bcad43d001e9e211d33eb5f9 (patch) | |
tree | 0767c1f4c875080015065d48fbd2e0f2ae5bcc31 | |
parent | 61aac0d2773f7a073b63661ec150954414f5696e (diff) | |
download | rust_rrule-11f9f1a7b2339898bcad43d001e9e211d33eb5f9.zip |
checkpoint
-rw-r--r-- | src/iter/iter_v2.rs | 12 | ||||
-rw-r--r-- | src/rrule.rs | 11 | ||||
-rw-r--r-- | src/rruleset.rs | 39 | ||||
-rw-r--r-- | src/rruleset_iter.rs | 238 |
4 files changed, 121 insertions, 179 deletions
diff --git a/src/iter/iter_v2.rs b/src/iter/iter_v2.rs index 609206d..2a7e361 100644 --- a/src/iter/iter_v2.rs +++ b/src/iter/iter_v2.rs @@ -29,8 +29,8 @@ impl Iterator for RRuleIter { } pub fn generate(iter: &mut RRuleIter) { - let options = iter.ii.options; - let count = options.count.unwrap_or(0); + let options = &iter.ii.options; + let mut count = options.count.unwrap_or(0); while iter.remain.is_empty() { let (dayset, start, end) = iter.ii.getdayset( @@ -143,9 +143,9 @@ pub fn generate(iter: &mut RRuleIter) { ); } - iter.ii.rebuild( - iter.counter_date.year() as isize, - iter.counter_date.month() as usize, - ); + let year = iter.counter_date.year(); + let month = iter.counter_date.month(); + + iter.ii.rebuild(year as isize, month as usize); } } diff --git a/src/rrule.rs b/src/rrule.rs index d372e06..0fa8f24 100644 --- a/src/rrule.rs +++ b/src/rrule.rs @@ -18,14 +18,15 @@ impl RRule { /// Returns all the recurrences of the rrule pub fn all(&mut self) -> Vec<DateTime<Tz>> { - self.into_iter().collect() + self.clone().into_iter().collect() } /// Returns the last recurrence before the given datetime instance. /// The inc keyword defines what happens if dt is an recurrence. /// With inc == true, if dt itself is an recurrence, it will be returned. pub fn before(&mut self, dt: DateTime<Tz>, inc: bool) -> Option<DateTime<Tz>> { - self.into_iter() + self.clone() + .into_iter() .take_while(|d| if inc { *d <= dt } else { *d < dt }) .last() } @@ -34,7 +35,8 @@ impl RRule { /// The inc keyword defines what happens if dt is an recurrence. /// With inc == true, if dt itself is an recurrence, it will be returned. pub fn after(&mut self, dt: DateTime<Tz>, inc: bool) -> Option<DateTime<Tz>> { - self.into_iter() + self.clone() + .into_iter() .skip_while(|d| if inc { *d <= dt } else { *d < dt }) .next() } @@ -49,7 +51,8 @@ impl RRule { before: DateTime<Tz>, inc: bool, ) -> Vec<DateTime<Tz>> { - self.into_iter() + self.clone() + .into_iter() .skip_while(|d| if inc { *d <= after } else { *d < after }) .take_while(|d| if inc { *d <= before } else { *d < before }) .collect() diff --git a/src/rruleset.rs b/src/rruleset.rs index e8ddf11..dcbfe59 100644 --- a/src/rruleset.rs +++ b/src/rruleset.rs @@ -1,13 +1,12 @@ use crate::datetime::DTime; use crate::options::RRuleParseError; use crate::rrule::RRule; -use crate::rruleset_iter::RRuleSetIter; use crate::rrulestr::build_rruleset; use chrono::prelude::*; use chrono_tz::{Tz, UTC}; use std::str::FromStr; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct RRuleSet { pub rrule: Vec<RRule>, pub rdate: Vec<DTime>, @@ -45,34 +44,27 @@ impl RRuleSet { /// Returns all the recurrences of the rruleset pub fn all(&mut self) -> Vec<DateTime<Tz>> { - let mut iter = RRuleSetIter::all(self); - iter.iter() + self.clone().into_iter().collect() } /// Returns the last recurrence before the given datetime instance. /// The inc keyword defines what happens if dt is an recurrence. /// With inc == true, if dt itself is an recurrence, it will be returned. - pub fn before(&mut self, date: DateTime<Tz>, inc: bool) -> Option<DateTime<Tz>> { - let mut iter = RRuleSetIter::before(self, date, inc); - let recurrences = iter.iter(); - if recurrences.is_empty() { - None - } else { - Some(recurrences[0]) - } + pub fn before(&mut self, dt: DateTime<Tz>, inc: bool) -> Option<DateTime<Tz>> { + self.clone() + .into_iter() + .take_while(|d| if inc { *d <= dt } else { *d < dt }) + .last() } /// Returns the last recurrence after the given datetime instance. /// The inc keyword defines what happens if dt is an recurrence. /// With inc == true, if dt itself is an recurrence, it will be returned. - pub fn after(&mut self, date: DateTime<Tz>, inc: bool) -> Option<DateTime<Tz>> { - let mut iter = RRuleSetIter::after(self, date, inc); - let recurrences = iter.iter(); - if recurrences.is_empty() { - None - } else { - Some(recurrences[0]) - } + pub fn after(&mut self, dt: DateTime<Tz>, inc: bool) -> Option<DateTime<Tz>> { + self.clone() + .into_iter() + .skip_while(|d| if inc { *d <= dt } else { *d < dt }) + .next() } /// Returns all the recurrences of the rrule between after and before. @@ -85,8 +77,11 @@ impl RRuleSet { before: DateTime<Tz>, inc: bool, ) -> Vec<DateTime<Tz>> { - let mut iter = RRuleSetIter::between(self, after, before, inc); - iter.iter() + self.clone() + .into_iter() + .skip_while(|d| if inc { *d <= after } else { *d < after }) + .take_while(|d| if inc { *d <= before } else { *d < before }) + .collect() } } diff --git a/src/rruleset_iter.rs b/src/rruleset_iter.rs index c9eca4e..9fe4c56 100644 --- a/src/rruleset_iter.rs +++ b/src/rruleset_iter.rs @@ -1,175 +1,119 @@ -use crate::iter::{iter, IterResult}; -use crate::rrule_iter::*; use crate::rruleset::RRuleSet; +use crate::{iter::RRuleIter, RRule}; use chrono::prelude::*; use chrono_tz::Tz; use std::collections::HashMap; +use std::iter::Iterator; -/// Result iterator for the RRuleSet type. It mostly just wraps -/// `RRuleIterRes` and also before accepting any date makes sure that -/// it does not collide with any exdates provided by either the exdates property -/// or any occurence produced by any of the the exrules. -pub struct RRuleSetIter<'a> { - exdate_hash: HashMap<i64, ()>, - iter_res: RRuleIterRes, - rrule_set: &'a mut RRuleSet, +pub struct RRuleIterSet { + pub queue: HashMap<usize, DateTime<Tz>>, + pub rrule_iters: Vec<RRuleIter>, + pub exrules: Vec<RRule>, + pub exdates: HashMap<i64, ()>, } -impl<'a> RRuleSetIter<'a> { - pub fn all(rrule_set: &'a mut RRuleSet) -> Self { - let iter_args = IterArgs { - inc: true, - before: None, - after: None, - dt: None, - }; - let iter_res = RRuleIterRes::new(QueryMethodTypes::All, iter_args); - - Self { - exdate_hash: HashMap::new(), - iter_res, - rrule_set, - } - } - - pub fn before(rrule_set: &'a mut RRuleSet, dt: DateTime<Tz>, inc: bool) -> Self { - let iter_args = IterArgs { - inc, - before: None, - after: None, - dt: Some(dt), - }; - let iter_res = RRuleIterRes::new(QueryMethodTypes::Before, iter_args); - - Self { - exdate_hash: HashMap::new(), - iter_res, - rrule_set, - } - } - - pub fn after(rrule_set: &'a mut RRuleSet, dt: DateTime<Tz>, inc: bool) -> Self { - let iter_args = IterArgs { - inc, - before: None, - after: None, - dt: Some(dt), - }; - let iter_res = RRuleIterRes::new(QueryMethodTypes::After, iter_args); - - Self { - exdate_hash: HashMap::new(), - iter_res, - rrule_set, - } - } - - pub fn between( - rrule_set: &'a mut RRuleSet, - after: DateTime<Tz>, - before: DateTime<Tz>, - inc: bool, - ) -> Self { - let iter_args = IterArgs { - inc, - before: Some(before), - after: Some(after), - dt: None, - }; - let iter_res = RRuleIterRes::new(QueryMethodTypes::Between, iter_args); - - Self { - exdate_hash: HashMap::new(), - iter_res, - rrule_set, - } - } - - /// Computes all exdates between after and before `DateTime`s for all the exrules and inserts them into - /// the exdate_hash. - pub fn eval_exdate(&mut self, after: &DateTime<Tz>, before: &DateTime<Tz>) { - for rrule in self.rrule_set.exrule.iter_mut() { - for date in rrule.between(after.clone(), before.clone(), true) { - self.exdate_hash.insert(date.timestamp(), ()); +impl Iterator for RRuleIterSet { + type Item = DateTime<Tz>; + + fn next(&mut self) -> Option<Self::Item> { + let mut next_date: Option<(usize, DateTime<Tz>)> = None; + + for (i, rrule_iter) in self.rrule_iters.iter().enumerate() { + let rrule_queue = self.queue.remove(&i); + let next_rrule_date = None; + match rrule_queue { + Some(d) => next_rrule_date = Some(d), + None => { + // should be generated + next_rrule_date = generate(rrule_iter, &self.exrules, &mut self.exdates); + } } - } - } - /// Here it is required to recompute exrules to see if some of the recurrences will collide with the provided date. - fn accept_when_unknown_bounds(&mut self, date: DateTime<Tz>) -> bool { - let dt = date.timestamp(); - if !self.exdate_hash.contains_key(&dt) { - self.eval_exdate( - &date.timezone().timestamp(dt - 1, 0), - &date.timezone().timestamp(dt + 1, 0), - ); - if !self.exdate_hash.contains_key(&dt) { - self.exdate_hash.insert(dt, ()); - return self.iter_res.accept(date); + match next_rrule_date { + Some(next_rrule_date) => match next_date { + None => next_date = Some((i, next_rrule_date)), + Some(date) => { + if date.1 < next_rrule_date { + // Add previous date to its rrule queue + self.queue.insert(date.0, date.1); + + // Update next_date + next_date = Some((i, next_rrule_date)); + } else { + // Store for next iterations + self.queue.insert(i, next_rrule_date); + } + } + }, + None => {} } } - true + next_date.map(|d| d.1) } +} - /// No need to recompute exrules as it has already been computed in the start of the iter method - /// because the bounds where known. - fn accept_when_known_bounds(&mut self, date: DateTime<Tz>) -> bool { - let dt = date.timestamp(); - if !self.exdate_hash.contains_key(&dt) { - self.exdate_hash.insert(dt, ()); - return self.iter_res.accept(date); - } - - true +fn generate( + rrule_iter: &RRuleIter, + exrules: &Vec<RRule>, + exdates: &mut HashMap<i64, ()>, +) -> Option<DateTime<Tz>> { + let mut date = rrule_iter.next(); + while !accept_generated_date(&date, exrules, exdates) { + date = rrule_iter.next(); } - pub fn iter(&mut self) -> Vec<DateTime<Tz>> { - // Add all exdates to exdate_hash - for date in &self.rrule_set.exdate { - self.exdate_hash.insert(date.timestamp(), ()); - } + date +} - // Small performance improvement by computing all exdates between - // before and after dates when. For all the other `QueryMethodTypes` - // the iter has to eval_exdate for every DateTime value produced by rdate or rrule - // to check if it conflicts with any exrule or exdate rules, this is because - // All, Before, After QueryMethodTypes has no time bounds and all the exdates can not be known beforehand. - match &self.iter_res.method { - QueryMethodTypes::Between => { - self.eval_exdate( - &self.iter_res.args.after.unwrap().clone(), - &self.iter_res.args.before.unwrap().clone(), - ); +fn accept_generated_date( + date: &Option<DateTime<Tz>>, + exrules: &Vec<RRule>, + exdates: &mut HashMap<i64, ()>, +) -> bool { + match date { + None => true, + Some(date) => { + let dt = date.timestamp(); + + if !exrules.is_empty() { + let after = date.timezone().timestamp(dt - 1, 0); + let before = date.timezone().timestamp(dt + 1, 0); + for exrule in exrules.iter_mut() { + for date in exrule.between(after, before, true) { + exdates.insert(date.timestamp(), ()); + } + } } - _ => (), - }; - for date in self.rrule_set.rdate.clone().into_iter() { - if !self.accept(date) { - break; + if !exdates.contains_key(&dt) { + return false; } - } - for rule in self.rrule_set.rrule.clone().iter_mut() { - iter(self, &mut rule.options); + true } - - let mut res = self.iter_res.get_value(); - res.sort(); - res } } -impl<'a> IterResult for RRuleSetIter<'a> { - fn accept(&mut self, date: DateTime<Tz>) -> bool { - match &self.iter_res.method { - QueryMethodTypes::Between => self.accept_when_known_bounds(date), - _ => self.accept_when_unknown_bounds(date), +impl IntoIterator for RRuleSet { + type Item = DateTime<Tz>; + + type IntoIter = RRuleIterSet; + + fn into_iter(self) -> Self::IntoIter { + RRuleIterSet { + queue: Default::default(), + rrule_iters: self + .rrule + .into_iter() + .map(|rrule| rrule.into_iter()) + .collect(), + exrules: self.exrule, + exdates: self + .exdate + .iter() + .map(|exdate| (exdate.timestamp(), ())) + .collect(), } } - - fn get_value(&self) -> Vec<DateTime<Tz>> { - self.iter_res.get_value() - } } |