//! The module containing the iterator types //! use chrono::NaiveDateTime; use crate::error::Error; use crate::error::Result; use crate::matcher::Matcher; use crate::timetype::TimeType; #[derive(Debug)] pub struct Iter { base: TimeType, increment: TimeType, had_first: bool, } // An iterator for creating new TimeType instances based on a base-date plus some increment value // // As the iterator only uses TimeType internally, the iterator performs very little computation // itself. It clones the `increment` one time per `next()` call, thought. // // Performing the computation on the yielded `TimeType` instances can be done by transforming this // iterator into a `CalculatingIter`. impl Iter { pub fn build(base: NaiveDateTime, inc: TimeType) -> Result { if !inc.is_a_amount() { Err(Error::ArgumentErrorNotAnAmount(inc)) } else { Ok(Iter { base: TimeType::moment(base), increment: inc, had_first: false, }) } } pub fn increment(&self) -> &TimeType { &self.increment } /// Skip one `next()` call pub fn skip(&mut self) -> Result<()> { self.base += self.increment.clone(); self.recalculate() } /// Redo the latest `next()` call with the next `next()` call pub fn rollback(&mut self) -> Result<()> { self.base -= self.increment.clone(); self.recalculate() } fn recalculate(&mut self) -> Result<()> { self.base.clone().calculate().map(|res| { self.base = res; }) } } /// # Warning /// /// As the iterator does not perform any computation, only wrapping objects into eachother, this is /// basically an endless iterator. /// /// As the TimeType internally uses `chrono::Duration` for storing durations, calling this without /// a bound may result in either a out-of-memory error or a panic because of overflow. /// /// Be warned. /// impl Iterator for Iter { type Item = Result; fn next(&mut self) -> Option { if !self.had_first { self.had_first = true; Some(Ok(self.base.clone())) } else { Some(self.skip().map(|_| self.base.clone())) } } } #[derive(Debug)] pub struct FilterIter(I, M) where I: Iterator>, M: Matcher; impl FilterIter where I: Iterator>, M: Matcher, { fn new(i: I, m: M) -> FilterIter { FilterIter(i, m) } } impl Iterator for FilterIter where I: Iterator>, M: Matcher, { type Item = Result; fn next(&mut self) -> Option { loop { match self.0.next() { None => return None, Some(Err(e)) => return Some(Err(e)), Some(Ok(tt)) => match self.1.matches(&tt) { Ok(false) => continue, Ok(true) => return Some(Ok(tt)), Err(e) => return Some(Err(e)), }, } } } } pub trait EveryFilter: Iterator> + Sized { fn every(self, matcher: M) -> FilterIter; } impl EveryFilter for I where I: Iterator>, M: Matcher, { fn every(self, matcher: M) -> FilterIter { FilterIter::new(self, matcher) } } #[derive(Debug)] pub struct WithoutIter(I, M) where I: Iterator>, M: Matcher; impl WithoutIter where I: Iterator>, M: Matcher, { fn new(i: I, m: M) -> WithoutIter { WithoutIter(i, m) } } impl Iterator for WithoutIter where I: Iterator>, M: Matcher, { type Item = Result; fn next(&mut self) -> Option { loop { match self.0.next() { None => return None, Some(Err(e)) => return Some(Err(e)), Some(Ok(tt)) => match self.1.matches(&tt) { Ok(false) => return Some(Ok(tt)), Ok(true) => continue, Err(e) => return Some(Err(e)), }, } } } } pub trait WithoutFilter: Iterator> + Sized { fn without(self, matcher: M) -> WithoutIter; } impl WithoutFilter for I where I: Iterator>, M: Matcher, { fn without(self, matcher: M) -> WithoutIter { WithoutIter::new(self, matcher) } } #[derive(Debug)] pub struct UntilIter(I, NaiveDateTime) where I: Iterator>; impl UntilIter where I: Iterator>, { fn new(i: I, date: NaiveDateTime) -> UntilIter { UntilIter(i, date) } } impl Iterator for UntilIter where I: Iterator>, { type Item = Result; fn next(&mut self) -> Option { match self.0.next() { None => None, Some(Err(e)) => Some(Err(e)), Some(Ok(tt)) => match tt.calculate() { Err(e) => Some(Err(e)), Ok(tt) => { if tt.is_moment() { if tt.get_moment().unwrap() < &self.1 { Some(Ok(tt)) } else { None } } else { Some(Err(Error::ArgumentErrorNotAMoment(tt.name()))) } }, }, } } } pub trait Until: Iterator> + Sized { fn until(self, ending: NaiveDateTime) -> UntilIter; } impl Until for I where I: Iterator>, { fn until(self, ending: NaiveDateTime) -> UntilIter { UntilIter::new(self, ending) } } #[derive(Debug)] pub struct TimesIter where I: Iterator>, { inner: I, times: i64, count: i64, } impl TimesIter where I: Iterator>, { fn new(i: I, times: i64) -> TimesIter { TimesIter { inner: i, times, count: 0, } } } impl Iterator for TimesIter where I: Iterator>, { type Item = Result; fn next(&mut self) -> Option { if self.times == self.count { None } else { self.count += 1; self.inner.next() } } } pub trait Times: Iterator> + Sized { fn times(self, times: i64) -> TimesIter; } impl Times for I where I: Iterator>, { fn times(self, times: i64) -> TimesIter { TimesIter::new(self, times) } } pub mod extensions { use crate::error::Error; use crate::error::Result; use crate::timetype::TimeType as TT; use super::Iter; pub trait Minutely { fn minutely(self, i: i64) -> Result; } pub trait Hourly { fn hourly(self, i: i64) -> Result; } pub trait Daily { fn daily(self, i: i64) -> Result; } pub trait Weekly: Sized { fn weekly(self, i: i64) -> Result; } pub trait Monthly { fn monthly(self, i: i64) -> Result; } pub trait Yearly { fn yearly(self, i: i64) -> Result; } pub trait Every { fn every(self, inc: TT) -> Result; } impl Minutely for TT { fn minutely(self, i: i64) -> Result { match self { TT::Moment(mom) => { let increment = TT::minutes(i); assert!(increment.is_a_amount(), "This is a Bug, please report this!"); Iter::build(mom, increment) }, _ => Err(Error::ArgumentErrorNotAnAmount(self)), } } } impl Hourly for TT { fn hourly(self, i: i64) -> Result { match self { TT::Moment(mom) => { let increment = TT::hours(i); assert!(increment.is_a_amount(), "This is a Bug, please report this!"); Iter::build(mom, increment) }, _ => Err(Error::ArgumentErrorNotAnAmount(self)), } } } impl Daily for TT { fn daily(self, i: i64) -> Result { match self { TT::Moment(mom) => { let increment = TT::days(i); assert!(increment.is_a_amount(), "This is a Bug, please report this!"); Iter::build(mom, increment) }, _ => Err(Error::ArgumentErrorNotAnAmount(self)), } } } impl Weekly for TT { /// Conveniance function over `Daily::daily( n * 7 )` fn weekly(self, i: i64) -> Result { match self { TT::Moment(mom) => { let increment = TT::days(i * 7); assert!(increment.is_a_amount(), "This is a Bug, please report this!"); Iter::build(mom, increment) }, _ => Err(Error::ArgumentErrorNotAnAmount(self)), } } } impl Monthly for TT { fn monthly(self, i: i64) -> Result { match self { TT::Moment(mom) => { let increment = TT::months(i); assert!(increment.is_a_amount(), "This is a Bug, please report this!"); Iter::build(mom, increment) }, _ => Err(Error::ArgumentErrorNotAnAmount(self)), } } } impl Yearly for TT { fn yearly(self, i: i64) -> Result { match self { TT::Moment(mom) => { let increment = TT::years(i); assert!(increment.is_a_amount(), "This is a Bug, please report this!"); Iter::build(mom, increment) }, _ => Err(Error::ArgumentErrorNotAnAmount(self)), } } } impl Every for TT { fn every(self, inc: TT) -> Result { match self { TT::Moment(mom) => Iter::build(mom, inc), _ => Err(Error::ArgumentErrorNotAnAmount(self)), } } } #[cfg(test)] mod tests { use chrono::NaiveDate as ND; use crate::timetype::TimeType as TT; use super::*; fn ymd_hms(y: i32, m: u32, d: u32, h: u32, mi: u32, s: u32) -> TT { TT::moment(ND::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, mi, s).unwrap()) } #[test] fn test_minutely() { let minutes = ymd_hms(2000, 1, 1, 0, 0, 0) .minutely(1) .unwrap() .take(5) .collect::>(); assert_eq!(ymd_hms(2000, 1, 1, 0, 0, 0), *minutes[0].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 1, 1, 0, 1, 0), *minutes[1].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 1, 1, 0, 2, 0), *minutes[2].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 1, 1, 0, 3, 0), *minutes[3].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 1, 1, 0, 4, 0), *minutes[4].as_ref().unwrap()); } #[test] fn test_hourly() { let minutes = ymd_hms(2000, 1, 1, 0, 0, 0) .hourly(1) .unwrap() .take(5) .collect::>(); assert_eq!(ymd_hms(2000, 1, 1, 0, 0, 0), *minutes[0].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 1, 1, 1, 0, 0), *minutes[1].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 1, 1, 2, 0, 0), *minutes[2].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 1, 1, 3, 0, 0), *minutes[3].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 1, 1, 4, 0, 0), *minutes[4].as_ref().unwrap()); } #[test] fn test_weekly() { let minutes = ymd_hms(2000, 1, 1, 1, 0, 0) .weekly(1) .unwrap() .take(5) .collect::>(); assert_eq!(ymd_hms(2000, 1, 1, 1, 0, 0), *minutes[0].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 1, 8, 1, 0, 0), *minutes[1].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 1, 15, 1, 0, 0), *minutes[2].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 1, 22, 1, 0, 0), *minutes[3].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 1, 29, 1, 0, 0), *minutes[4].as_ref().unwrap()); } #[test] fn test_monthly() { let minutes = ymd_hms(2000, 1, 1, 0, 0, 0) .monthly(1) .unwrap() .take(5) .collect::>(); assert_eq!(ymd_hms(2000, 1, 1, 0, 0, 0), *minutes[0].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 2, 1, 0, 0, 0), *minutes[1].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 3, 1, 0, 0, 0), *minutes[2].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 4, 1, 0, 0, 0), *minutes[3].as_ref().unwrap()); assert_eq!(ymd_hms(2000, 5, 1, 0, 0, 0), *minutes[4].as_ref().unwrap()); } #[test] fn test_yearly() { let minutes = ymd_hms(2000, 1, 1, 0, 0, 0) .yearly(1) .unwrap() .take(5) .collect::>(); assert_eq!(ymd_hms(2000, 1, 1, 0, 0, 0), *minutes[0].as_ref().unwrap()); assert_eq!(ymd_hms(2001, 1, 1, 0, 0, 0), *minutes[1].as_ref().unwrap()); assert_eq!(ymd_hms(2002, 1, 1, 0, 0, 0), *minutes[2].as_ref().unwrap()); assert_eq!(ymd_hms(2003, 1, 1, 0, 0, 0), *minutes[3].as_ref().unwrap()); assert_eq!(ymd_hms(2004, 1, 1, 0, 0, 0), *minutes[4].as_ref().unwrap()); } } } #[cfg(test)] mod type_tests { use super::extensions::*; use super::*; #[test] fn test_iterator_every_once() { // This test is solely to check whether this compiles and the API is nice let _ = TimeType::today() .yearly(1) .unwrap() .every(crate::indicator::Day::Monday); } #[test] fn test_iterator_every_twice() { // This test is solely to check whether this compiles and the API is nice let _ = TimeType::today() .yearly(1) // collecting makes us stack-overflow because of the heavy filtering! .unwrap() .every(crate::indicator::Day::Monday) .every(crate::indicator::Month::January); } } #[cfg(all(feature = "with-filters", test))] mod type_tests_filter_interface { use filters::filter::Filter; use filters::filter::IntoFilter; use super::extensions::*; use super::*; #[test] fn test_compile() { // This test is solely to check whether this compiles and the API is nice let _ = TimeType::today() .daily(1) .unwrap() .every( crate::indicator::Day::Monday .into_filter() .or(crate::indicator::Month::January), ) .take(12) .collect::>(); } #[test] fn test_compile_skip() { // This test is solely to check whether this compiles and the API is nice let v = TimeType::today() .daily(1) .unwrap() .take(20) .every(crate::indicator::Day::Monday) .without(crate::indicator::Day::Monday) .collect::>(); assert_eq!(0, v.len()); } } #[cfg(test)] mod test_until { use super::extensions::*; use super::*; #[test] fn test_until() { let yesterday = *(TimeType::today() - TimeType::days(1)) .calculate() .unwrap() .get_moment() .unwrap(); let v = TimeType::today().daily(1).unwrap().until(yesterday).collect::>(); assert!(v.is_empty()); } #[test] fn test_until_1() { let end = *(TimeType::today() + TimeType::days(1)) .calculate() .unwrap() .get_moment() .unwrap(); let v = TimeType::today().daily(1).unwrap().until(end).collect::>(); assert_eq!(v.len(), 1); } #[test] fn test_until_2() { let end = *(TimeType::today() + TimeType::days(2)) .calculate() .unwrap() .get_moment() .unwrap(); let v = TimeType::today().hourly(1).unwrap().until(end).collect::>(); assert_eq!(v.len(), 48); } }