diff options
author | Fredrik Meringdal <fredrikmeringdal@Fredriks-MacBook-Pro.local> | 2021-02-03 17:55:43 +0100 |
---|---|---|
committer | Fredrik Meringdal <fredrikmeringdal@Fredriks-MacBook-Pro.local> | 2021-02-03 17:55:43 +0100 |
commit | 61aac0d2773f7a073b63661ec150954414f5696e (patch) | |
tree | 739b82650bafd04cdd29ca7aa4f2af94c6afb1b8 | |
parent | 4b12b6328955612dcb4a248a14d64864d9aecdf1 (diff) | |
download | rust_rrule-61aac0d2773f7a073b63661ec150954414f5696e.zip |
iterator for rrule
-rw-r--r-- | src/iter/iter_v2.rs | 151 | ||||
-rw-r--r-- | src/iter/iterinfo.rs | 12 | ||||
-rw-r--r-- | src/iter/mod.rs | 258 | ||||
-rw-r--r-- | src/rrule.rs | 59 | ||||
-rw-r--r-- | src/rrule_iter.rs | 64 |
5 files changed, 337 insertions, 207 deletions
diff --git a/src/iter/iter_v2.rs b/src/iter/iter_v2.rs new file mode 100644 index 0000000..609206d --- /dev/null +++ b/src/iter/iter_v2.rs @@ -0,0 +1,151 @@ +use crate::{datetime::Time, Frequenzy}; +use chrono::prelude::*; +use chrono_tz::Tz; + +use super::{build_poslist, from_ordinal, increment_counter_date, remove_filtered_days, IterInfo}; + +pub struct RRuleIter { + pub counter_date: DateTime<Tz>, + pub ii: IterInfo, + pub timeset: Vec<Time>, + pub remain: Vec<DateTime<Tz>>, + pub finished: bool, +} + +impl Iterator for RRuleIter { + type Item = DateTime<Tz>; + + fn next(&mut self) -> Option<Self::Item> { + if !self.finished { + generate(self); + } + + if self.remain.is_empty() { + return None; + } + + Some(self.remain.remove(0)) + } +} + +pub fn generate(iter: &mut RRuleIter) { + let options = iter.ii.options; + let count = options.count.unwrap_or(0); + + while iter.remain.is_empty() { + let (dayset, start, end) = iter.ii.getdayset( + &iter.ii.options.freq, + iter.counter_date.year() as isize, + iter.counter_date.month() as usize, + iter.counter_date.day() as usize, + ); + + let mut dayset = dayset + .into_iter() + .map(|s| Some(s as isize)) + .collect::<Vec<Option<isize>>>(); + + let filtered = remove_filtered_days(&mut dayset, start, end, &iter.ii); + + if options.bysetpos.len() > 0 { + let poslist = build_poslist( + &options.bysetpos, + &iter.timeset, + start, + end, + &iter.ii, + &dayset, + &options.tzid, + ); + + for j in 0..poslist.len() { + let res = poslist[j]; + if options.until.is_some() && res > options.until.unwrap() { + // return iter_result.get_value(); + continue; // or break ? + } + + if res >= options.dtstart { + iter.remain.push(res); + + if count > 0 { + count -= 1; + if count == 0 { + return; + } + } + } + } + } else { + for j in start..end { + let current_day = dayset[j]; + if current_day.is_none() { + continue; + } + + let current_day = current_day.unwrap(); + let date = + from_ordinal(iter.ii.yearordinal().unwrap() + current_day, &options.tzid); + for k in 0..iter.timeset.len() { + let res = options + .tzid + .ymd(date.year(), date.month(), date.day()) + .and_hms( + iter.timeset[k].hour as u32, + iter.timeset[k].minute as u32, + iter.timeset[k].second as u32, + ); + if options.until.is_some() && res > options.until.unwrap() { + return; + } + if res >= options.dtstart { + iter.remain.push(res); + // let (add, continue_iterator) = iter_result.accept(&res); + // if add { + // iter.remain.push(res); + // } + // if !continue_iterator { + // return; + // } + + if count > 0 { + count -= 1; + if count == 0 { + return; + } + } + } + } + } + } + + if options.interval == 0 { + return; + } + + // Handle frequency and interval + iter.counter_date = increment_counter_date(iter.counter_date, &options, filtered); + + if iter.counter_date.year() > 2200 { + return; + } + + if options.freq == Frequenzy::Hourly + || options.freq == Frequenzy::Minutely + || options.freq == Frequenzy::Secondly + { + iter.timeset = iter.ii.gettimeset( + &options.freq, + iter.counter_date.hour() as usize, + iter.counter_date.minute() as usize, + iter.counter_date.second() as usize, + 0, + ); + } + + iter.ii.rebuild( + iter.counter_date.year() as isize, + iter.counter_date.month() as usize, + ); + } +} diff --git a/src/iter/iterinfo.rs b/src/iter/iterinfo.rs index 7c47645..048632b 100644 --- a/src/iter/iterinfo.rs +++ b/src/iter/iterinfo.rs @@ -5,15 +5,15 @@ use crate::iter::yearinfo::{rebuild_year, YearInfo}; use crate::options::{Frequenzy, ParsedOptions}; use chrono::prelude::*; -pub struct IterInfo<'a> { +pub struct IterInfo { pub yearinfo: Option<YearInfo>, pub monthinfo: Option<MonthInfo>, pub eastermask: Option<Vec<isize>>, - options: &'a ParsedOptions, + pub options: ParsedOptions, } -impl<'a> IterInfo<'a> { - pub fn new(options: &'a ParsedOptions) -> Self { +impl IterInfo { + pub fn new(options: ParsedOptions) -> Self { Self { options, yearinfo: None, @@ -24,7 +24,7 @@ impl<'a> IterInfo<'a> { pub fn rebuild(&mut self, year: isize, month: usize) { if self.monthinfo.is_none() || year != self.monthinfo.as_ref().unwrap().lastyear { - self.yearinfo = Some(rebuild_year(year as i32, self.options)); + self.yearinfo = Some(rebuild_year(year as i32, &self.options)); } if !self.options.bynweekday.is_empty() @@ -38,7 +38,7 @@ impl<'a> IterInfo<'a> { yearinfo.yearlen, &yearinfo.mrange, &yearinfo.wdaymask, - self.options, + &self.options, )); } } diff --git a/src/iter/mod.rs b/src/iter/mod.rs index dc7c51d..419dcbd 100644 --- a/src/iter/mod.rs +++ b/src/iter/mod.rs @@ -1,11 +1,13 @@ mod iterinfo; mod monthinfo; mod yearinfo; -use iterinfo::IterInfo; +pub use iterinfo::IterInfo; mod poslist; use poslist::build_poslist; mod easter; +mod iter_v2; mod masks; +pub use iter_v2::{generate, RRuleIter}; use crate::datetime::{from_ordinal, get_weekday_val, DTime, Time}; use crate::options::*; @@ -15,134 +17,133 @@ use chrono::prelude::*; use chrono::Duration; pub trait IterResult { - fn accept(&mut self, date: DTime) -> bool; - fn get_value(&self) -> Vec<DTime>; + fn accept(&self, date: &DTime) -> (bool, bool); } -pub fn iter<T: IterResult>(iter_result: &mut T, options: &mut ParsedOptions) -> Vec<DTime> { - if (options.count.is_some() && options.count.unwrap() == 0) || options.interval == 0 { - return iter_result.get_value(); - } - - let mut counter_date = options.dtstart; - let mut ii = IterInfo::new(options); - ii.rebuild(counter_date.year() as isize, counter_date.month() as usize); - - let mut timeset = make_timeset(&ii, &counter_date, options); - let mut count = match options.count { - Some(count) => count, - _ => 0, - }; - - loop { - let (dayset, start, end) = ii.getdayset( - &options.freq, - counter_date.year() as isize, - counter_date.month() as usize, - counter_date.day() as usize, - ); - - let mut dayset = dayset - .into_iter() - .map(|s| Some(s as isize)) - .collect::<Vec<Option<isize>>>(); - - let filtered = remove_filtered_days(&mut dayset, start, end, &ii, options); - - if not_empty(&options.bysetpos) { - let poslist = build_poslist( - &options.bysetpos, - ×et, - start, - end, - &ii, - &dayset, - &options.tzid, - ); - - for j in 0..poslist.len() { - let res = poslist[j]; - if options.until.is_some() && res > options.until.unwrap() { - return iter_result.get_value(); - } - - if res >= options.dtstart { - if !iter_result.accept(res) { - return iter_result.get_value(); - } - - if count > 0 { - count -= 1; - if count == 0 { - return iter_result.get_value(); - } - } - } - } - } else { - for j in start..end { - let current_day = dayset[j]; - if current_day.is_none() { - continue; - } - - let current_day = current_day.unwrap(); - let date = from_ordinal(ii.yearordinal().unwrap() + current_day, &options.tzid); - for k in 0..timeset.len() { - let res = options - .tzid - .ymd(date.year(), date.month(), date.day()) - .and_hms( - timeset[k].hour as u32, - timeset[k].minute as u32, - timeset[k].second as u32, - ); - if options.until.is_some() && res > options.until.unwrap() { - return iter_result.get_value(); - } - if res >= options.dtstart { - if !iter_result.accept(res) { - return iter_result.get_value(); - } - - if count > 0 { - count -= 1; - if count == 0 { - return iter_result.get_value(); - } - } - } - } - } - } - - if options.interval == 0 { - return iter_result.get_value(); - } - - // Handle frequency and interval - counter_date = increment_counter_date(counter_date, options, filtered); - - if counter_date.year() > 2200 { - return iter_result.get_value(); - } - - if options.freq == Frequenzy::Hourly - || options.freq == Frequenzy::Minutely - || options.freq == Frequenzy::Secondly - { - timeset = ii.gettimeset( - &options.freq, - counter_date.hour() as usize, - counter_date.minute() as usize, - counter_date.second() as usize, - 0, - ); - } - - ii.rebuild(counter_date.year() as isize, counter_date.month() as usize); - } -} +// pub fn iter<T: IterResult>(iter_result: &mut T, options: &mut ParsedOptions) -> Vec<DTime> { +// if (options.count.is_some() && options.count.unwrap() == 0) || options.interval == 0 { +// return iter_result.get_value(); +// } + +// let mut counter_date = options.dtstart; +// let mut ii = IterInfo::new(options); +// ii.rebuild(counter_date.year() as isize, counter_date.month() as usize); + +// let mut timeset = make_timeset(&ii, &counter_date, options); +// let mut count = match options.count { +// Some(count) => count, +// _ => 0, +// }; + +// loop { +// let (dayset, start, end) = ii.getdayset( +// &options.freq, +// counter_date.year() as isize, +// counter_date.month() as usize, +// counter_date.day() as usize, +// ); + +// let mut dayset = dayset +// .into_iter() +// .map(|s| Some(s as isize)) +// .collect::<Vec<Option<isize>>>(); + +// let filtered = remove_filtered_days(&mut dayset, start, end, &ii, options); + +// if not_empty(&options.bysetpos) { +// let poslist = build_poslist( +// &options.bysetpos, +// ×et, +// start, +// end, +// &ii, +// &dayset, +// &options.tzid, +// ); + +// for j in 0..poslist.len() { +// let res = poslist[j]; +// if options.until.is_some() && res > options.until.unwrap() { +// return iter_result.get_value(); +// } + +// if res >= options.dtstart { +// if !iter_result.accept(res) { +// return iter_result.get_value(); +// } + +// if count > 0 { +// count -= 1; +// if count == 0 { +// return iter_result.get_value(); +// } +// } +// } +// } +// } else { +// for j in start..end { +// let current_day = dayset[j]; +// if current_day.is_none() { +// continue; +// } + +// let current_day = current_day.unwrap(); +// let date = from_ordinal(ii.yearordinal().unwrap() + current_day, &options.tzid); +// for k in 0..timeset.len() { +// let res = options +// .tzid +// .ymd(date.year(), date.month(), date.day()) +// .and_hms( +// timeset[k].hour as u32, +// timeset[k].minute as u32, +// timeset[k].second as u32, +// ); +// if options.until.is_some() && res > options.until.unwrap() { +// return iter_result.get_value(); +// } +// if res >= options.dtstart { +// if !iter_result.accept(res) { +// return iter_result.get_value(); +// } + +// if count > 0 { +// count -= 1; +// if count == 0 { +// return iter_result.get_value(); +// } +// } +// } +// } +// } +// } + +// if options.interval == 0 { +// return iter_result.get_value(); +// } + +// // Handle frequency and interval +// counter_date = increment_counter_date(counter_date, options, filtered); + +// if counter_date.year() > 2200 { +// return iter_result.get_value(); +// } + +// if options.freq == Frequenzy::Hourly +// || options.freq == Frequenzy::Minutely +// || options.freq == Frequenzy::Secondly +// { +// timeset = ii.gettimeset( +// &options.freq, +// counter_date.hour() as usize, +// counter_date.minute() as usize, +// counter_date.second() as usize, +// 0, +// ); +// } + +// ii.rebuild(counter_date.year() as isize, counter_date.month() as usize); +// } +// } pub fn increment_counter_date( counter_date: DTime, @@ -301,14 +302,13 @@ pub fn remove_filtered_days( start: usize, end: usize, ii: &IterInfo, - options: &ParsedOptions, ) -> bool { let mut filtered = false; for daycounter in start..end { match dayset[daycounter] { Some(current_day) => { - filtered = is_filtered(ii, current_day as usize, options); + filtered = is_filtered(ii, current_day as usize, &ii.options); if filtered { dayset[daycounter] = None; } diff --git a/src/rrule.rs b/src/rrule.rs index c2a080e..d372e06 100644 --- a/src/rrule.rs +++ b/src/rrule.rs @@ -1,4 +1,4 @@ -use crate::iter::iter; +use crate::iter::{generate, IterInfo}; use crate::options::*; use crate::rrule_iter::*; use crate::rrulestr::build_rrule; @@ -18,55 +18,25 @@ impl RRule { /// Returns all the recurrences of the rrule pub fn all(&mut self) -> Vec<DateTime<Tz>> { - let iter_args = IterArgs { - inc: true, - before: None, - after: None, - dt: None, - }; - let mut iter_res = RRuleIterRes::new(QueryMethodTypes::All, iter_args); - - let res = iter(&mut iter_res, &mut self.options); - res + self.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>> { - let iter_args = IterArgs { - inc, - before: None, - after: None, - dt: Some(dt), - }; - let mut iter_res = RRuleIterRes::new(QueryMethodTypes::Before, iter_args); - - let recurrences = iter(&mut iter_res, &mut self.options); - if recurrences.is_empty() { - None - } else { - Some(recurrences[0]) - } + self.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, dt: DateTime<Tz>, inc: bool) -> Option<DateTime<Tz>> { - let iter_args = IterArgs { - inc, - before: None, - after: None, - dt: Some(dt), - }; - let mut iter_res = RRuleIterRes::new(QueryMethodTypes::After, iter_args); - let recurrences = iter(&mut iter_res, &mut self.options); - if recurrences.is_empty() { - None - } else { - Some(recurrences[0]) - } + self.into_iter() + .skip_while(|d| if inc { *d <= dt } else { *d < dt }) + .next() } /// Returns all the recurrences of the rrule between after and before. @@ -79,15 +49,10 @@ impl RRule { before: DateTime<Tz>, inc: bool, ) -> Vec<DateTime<Tz>> { - let iter_args = IterArgs { - inc, - before: Some(before), - after: Some(after), - dt: None, - }; - let mut iter_res = RRuleIterRes::new(QueryMethodTypes::Between, iter_args); - - iter(&mut iter_res, &mut self.options) + self.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/rrule_iter.rs b/src/rrule_iter.rs index 7751476..1b45a77 100644 --- a/src/rrule_iter.rs +++ b/src/rrule_iter.rs @@ -1,4 +1,7 @@ -use crate::iter::IterResult; +use crate::{ + iter::{make_timeset, IterInfo, IterResult, RRuleIter}, + RRule, +}; use chrono::prelude::*; use chrono::Duration; use chrono_tz::Tz; @@ -60,34 +63,45 @@ impl RRuleIterRes { } impl IterResult for RRuleIterRes { - fn accept(&mut self, date: DateTime<Tz>) -> bool { - self.total += 1; - let too_early = self.min_date.is_some() && date < self.min_date.unwrap(); - let too_late = self.max_date.is_some() && date > self.max_date.unwrap(); - + // Returns tuple of flags indicating whether to add and continue + // iteration (add_date, continue_iteration) + fn accept(&self, date: &DateTime<Tz>) -> (bool, bool) { + let too_early = match self.min_date { + Some(d) => d > *date, + None => false, + }; + let too_late = match self.max_date { + Some(d) => d < *date, + None => false, + }; match self.method { - QueryMethodTypes::Between if too_early => true, - QueryMethodTypes::Between if too_late => false, - QueryMethodTypes::Before if too_late => false, - QueryMethodTypes::After if too_early => true, - QueryMethodTypes::After => { - self.add(date); - false - } - _ => self.add(date), + QueryMethodTypes::Between if too_early => (false, true), + QueryMethodTypes::Between if too_late => (false, false), + QueryMethodTypes::Before if too_late => (false, false), + QueryMethodTypes::After => (!too_early, too_early), + _ => (true, true), } } +} - // before and after returns only one date whereas all and between an array - fn get_value(&self) -> Vec<DateTime<Tz>> { - match self.method { - QueryMethodTypes::Between | QueryMethodTypes::All => self.result.clone(), - _ => { - if self.result.is_empty() { - return vec![]; - } - vec![self.result[self.result.len() - 1].clone()] - } +impl IntoIterator for RRule { + type Item = DateTime<Tz>; + + type IntoIter = RRuleIter; + + fn into_iter(self) -> Self::IntoIter { + let mut ii = IterInfo::new(self.options); + let mut counter_date = ii.options.dtstart; + ii.rebuild(counter_date.year() as isize, counter_date.month() as usize); + + let timeset = make_timeset(&ii, &counter_date, &ii.options); + + RRuleIter { + counter_date, + ii, + timeset, + remain: vec![], + finished: false, } } } |