From d723826682c85abb545b7d20ec461e9e64f740da Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 11 Nov 2017 11:18:52 +0100 Subject: Add error for calculating non-date inside iterator parsing --- src/error.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/error.rs b/src/error.rs index 653b368..b9e7949 100644 --- a/src/error.rs +++ b/src/error.rs @@ -59,6 +59,11 @@ error_chain! { display("Cannot compare Month to non-Moment TimeType: {:?}", tt_rep) } + NotADateInsideIterator { + description("Cannot calculate date for iterator") + display("Cannot calculate date for iterator") + } + UnknownParserError { description("Unknown parser error") display("Unknown parser error") -- cgit v1.2.3 From 3a4f59eafe6e8a4e00d0a3aa0fe1853a3351adaa Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 11 Nov 2017 11:19:11 +0100 Subject: Add "Times" iterator helper type --- src/iter.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/iter.rs b/src/iter.rs index 64c6ba1..7399de5 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -222,6 +222,53 @@ impl Until for I } } +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: 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, i64) -> TimesIter; +} + +impl Times for I + where I: Iterator> +{ + fn times(self, times: i64) -> TimesIter { + TimesIter::new(self, times) + } +} + pub mod extensions { use timetype::TimeType as TT; use super::Iter; -- cgit v1.2.3 From c1a9ab6a43d14b007f9787d9eb48752b13ac7dc4 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 11 Nov 2017 11:19:42 +0100 Subject: Add parser iterator type -> API iterator type mapping --- src/iter.rs | 5 ++++ src/parser.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/iter.rs b/src/iter.rs index 7399de5..05669c2 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -9,6 +9,7 @@ use error::Result; use timetype::TimeType; use matcher::Matcher; +#[derive(Debug)] pub struct Iter { base: TimeType, increment: TimeType, @@ -80,6 +81,7 @@ impl Iterator for Iter { } +#[derive(Debug)] pub struct FilterIter(I, M) where I: Iterator>, M: Matcher; @@ -127,6 +129,7 @@ impl EveryFilter for I } } +#[derive(Debug)] pub struct WithoutIter(I, M) where I: Iterator>, M: Matcher; @@ -174,6 +177,7 @@ impl WithoutFilter for I } } +#[derive(Debug)] pub struct UntilIter(I, NaiveDateTime) where I: Iterator>; @@ -222,6 +226,7 @@ impl Until for I } } +#[derive(Debug)] pub struct TimesIter where I: Iterator> { diff --git a/src/parser.rs b/src/parser.rs index 7a2e7a1..80ff151 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -50,6 +50,8 @@ use std::str::FromStr; use chrono::NaiveDate; use timetype; +use iter; +use error; named!(integer, alt!( map_res!( @@ -345,18 +347,92 @@ pub enum UntilSpec { } named!(iterator, do_parse!( - d: date >> - spec: iter_spec >> - until: opt!(until_spec) >> + opt!(sp) >> d: date >> + opt!(sp) >> spec: iter_spec >> + opt!(sp) >> until: opt!(complete!(until_spec)) >> (Iterator(d, spec, until)) )); #[derive(Debug, PartialEq, Eq)] pub struct Iterator(Date, Iterspec, Option); -impl Into for Iterator { - fn into(self) -> timetype::TimeType { - unimplemented!() +impl Iterator { + pub fn into_user_iterator(self) -> error::Result> { + use iter::Times; + use iter::Until; + + let unit_to_amount = |i, unit| match unit { + Unit::Second => timetype::TimeType::seconds(i), + Unit::Minute => timetype::TimeType::minutes(i), + Unit::Hour => timetype::TimeType::hours(i), + Unit::Day => timetype::TimeType::days(i), + Unit::Week => timetype::TimeType::weeks(i), + Unit::Month => timetype::TimeType::months(i), + Unit::Year => timetype::TimeType::years(i), + }; + + let recur = match self.1 { + Iterspec::Every(i, unit) => unit_to_amount(i, unit), + Iterspec::Secondly => unit_to_amount(1, Unit::Second), + Iterspec::Minutely => unit_to_amount(1, Unit::Minute), + Iterspec::Hourly => unit_to_amount(1, Unit::Hour), + Iterspec::Daily => unit_to_amount(1, Unit::Day), + Iterspec::Weekly => unit_to_amount(1, Unit::Week), + Iterspec::Monthly => unit_to_amount(1, Unit::Month), + Iterspec::Yearly => unit_to_amount(1, Unit::Year), + }; + + let into_ndt = |e: timetype::TimeType| try!(e.calculate()) + .get_moment() + .ok_or(error::KairosErrorKind::NotADateInsideIterator) + .map(Clone::clone); + + match self.2 { + Some(UntilSpec::Exact(e)) => { + let base = try!(into_ndt(self.0.into())); + let e = try!(into_ndt(e.into())); + + iter::Iter::build(base, recur) + .map(|it| UserIterator::UntilIterator(it.until(e))) + }, + + Some(UntilSpec::Times(i)) => { + let base = try!(into_ndt(self.0.into())); + iter::Iter::build(base, recur) + .map(|it| it.times(i)) + .map(UserIterator::TimesIter) + }, + + None => { + let base = try!(into_ndt(self.0.into())); + iter::Iter::build(base, recur) + .map(UserIterator::Iterator) + }, + } + } +} + +// names are hard +#[derive(Debug)] +pub enum UserIterator + where I: ::std::iter::Iterator> +{ + Iterator(iter::Iter), + TimesIter(iter::TimesIter), + UntilIterator(iter::UntilIter) +} + +impl ::std::iter::Iterator for UserIterator + where I: ::std::iter::Iterator> +{ + type Item = error::Result; + + fn next(&mut self) -> Option { + match *self { + UserIterator::Iterator(ref mut i) => i.next(), + UserIterator::TimesIter(ref mut i) => i.next(), + UserIterator::UntilIterator(ref mut i) => i.next(), + } } } -- cgit v1.2.3 From 68d423f4738202e608909aa18be4f14863466ef8 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 19 Nov 2017 13:23:33 +0100 Subject: Add more tests for UntilIter --- src/iter.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/iter.rs b/src/iter.rs index 05669c2..b253f9f 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -580,5 +580,41 @@ mod test_until { assert!(v.is_empty()); } + #[test] + fn test_until_1() { + let end = (TimeType::today() + TimeType::days(2)) + .calculate() + .unwrap() + .get_moment() + .unwrap() + .clone(); + + 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() + .clone(); + + let v = TimeType::today() + .hourly(1) + .unwrap() + .until(end) + .collect::>(); + + assert_eq!(v.len(), 48 - 1); // -1 because we do not start to iterate with 0, but 1 + } + } -- cgit v1.2.3 From 0d2ed30311b8b11b45161586d4fd49f595696ecb Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 19 Nov 2017 13:40:28 +0100 Subject: Add tests for iterators --- src/parser.rs | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index 80ff151..723f4f3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -774,5 +774,229 @@ mod tests { assert_eq!(calc_res.get_years(), 0); } + #[test] + fn test_iterator_1() { + let res = iterator(&b"2017-01-01 hourly"[..]); + assert!(res.is_done(), format!("Not done: {:?}", res)); + let (_, i) = res.unwrap(); + println!("{:#?}", i); + + let ui : Result, _> = i.into_user_iterator(); + assert!(ui.is_ok(), "Not okay: {:#?}", ui); + let mut ui = ui.unwrap(); + + for hour in 1..10 { // 10 is randomly chosen (fair dice roll... ) + let n = ui.next().unwrap(); + assert!(n.is_ok(), "Not ok: {:#?}", n); + let tt = n.unwrap(); + assert_eq!(tt.get_moment().unwrap().year() , 2017); + assert_eq!(tt.get_moment().unwrap().month() , 01); + assert_eq!(tt.get_moment().unwrap().day() , 01); + assert_eq!(tt.get_moment().unwrap().hour() , hour); + assert_eq!(tt.get_moment().unwrap().minute(), 00); + assert_eq!(tt.get_moment().unwrap().second(), 00); + } + } + + #[test] + fn test_iterator_2() { + let res = iterator(&b"2017-01-01 every 2mins"[..]); + assert!(res.is_done(), format!("Not done: {:?}", res)); + let (_, i) = res.unwrap(); + println!("{:#?}", i); + + let ui : Result, _> = i.into_user_iterator(); + assert!(ui.is_ok(), "Not okay: {:#?}", ui); + let mut ui = ui.unwrap(); + + for min in (1..60).into_iter().filter(|n| n % 2 == 0) { + let n = ui.next().unwrap(); + assert!(n.is_ok(), "Not ok: {:#?}", n); + let tt = n.unwrap(); + assert_eq!(tt.get_moment().unwrap().year() , 2017); + assert_eq!(tt.get_moment().unwrap().month() , 01); + assert_eq!(tt.get_moment().unwrap().day() , 01); + assert_eq!(tt.get_moment().unwrap().hour() , 00); + assert_eq!(tt.get_moment().unwrap().minute(), min); + assert_eq!(tt.get_moment().unwrap().second(), 00); + } + } + + #[test] + fn test_iterator_3() { + let res = iterator(&b"2017-01-01 daily"[..]); + assert!(res.is_done(), format!("Not done: {:?}", res)); + let (_, i) = res.unwrap(); + println!("{:#?}", i); + + let ui : Result, _> = i.into_user_iterator(); + assert!(ui.is_ok(), "Not okay: {:#?}", ui); + let mut ui = ui.unwrap(); + + for day in 2..30 { + let n = ui.next().unwrap(); + assert!(n.is_ok(), "Not ok: {:#?}", n); + let tt = n.unwrap(); + assert_eq!(tt.get_moment().unwrap().year() , 2017); + assert_eq!(tt.get_moment().unwrap().month() , 01); + assert_eq!(tt.get_moment().unwrap().day() , day); + assert_eq!(tt.get_moment().unwrap().hour() , 00); + assert_eq!(tt.get_moment().unwrap().minute(), 00); + assert_eq!(tt.get_moment().unwrap().second(), 00); + } + } + + #[test] + fn test_iterator_4() { + let res = iterator(&b"2017-01-01 weekly"[..]); + assert!(res.is_done(), format!("Not done: {:?}", res)); + let (_, i) = res.unwrap(); + println!("{:#?}", i); + + let ui : Result, _> = i.into_user_iterator(); + assert!(ui.is_ok(), "Not okay: {:#?}", ui); + let mut ui = ui.unwrap(); + + for week in 1..3 { + let n = ui.next().unwrap(); + assert!(n.is_ok(), "Not ok: {:#?}", n); + let tt = n.unwrap(); + assert_eq!(tt.get_moment().unwrap().year() , 2017); + assert_eq!(tt.get_moment().unwrap().month() , 01); + assert_eq!(tt.get_moment().unwrap().day() , 01 + (week * 7)); + assert_eq!(tt.get_moment().unwrap().hour() , 00); + assert_eq!(tt.get_moment().unwrap().minute(), 00); + assert_eq!(tt.get_moment().unwrap().second(), 00); + } + } + + #[test] + fn test_until_spec_1() { + let res = until_spec(&b"until 2017-01-01T05:00:00"[..]); + assert!(res.is_done(), format!("Not done: {:?}", res)); + let (_, i) = res.unwrap(); + println!("{:#?}", i); + } + + #[test] + fn test_until_iterator_1() { + let res = iterator(&b"2017-01-01 hourly until 2017-01-01T05:00:00"[..]); + assert!(res.is_done(), format!("Not done: {:?}", res)); + let (_, i) = res.unwrap(); + println!("{:#?}", i); + + let ui : Result, _> = i.into_user_iterator(); + assert!(ui.is_ok(), "Not okay: {:#?}", ui); + let mut ui = ui.unwrap(); + println!("Okay: {:#?}", ui); + + for hour in 1..10 { // 10 is randomly chosen (fair dice roll... ) + if hour > 5 - 1 { + let n = ui.next(); + assert!(n.is_none(), "Is Some, should be None: {:?}", n); + return; + } else { + let n = ui.next().unwrap(); + assert!(n.is_ok(), "Not ok: {:#?}", n); + let tt = n.unwrap(); + assert_eq!(tt.get_moment().unwrap().year() , 2017); + assert_eq!(tt.get_moment().unwrap().month() , 01); + assert_eq!(tt.get_moment().unwrap().day() , 01); + assert_eq!(tt.get_moment().unwrap().hour() , hour); + assert_eq!(tt.get_moment().unwrap().minute(), 00); + assert_eq!(tt.get_moment().unwrap().second(), 00); + } + } + } + + #[test] + fn test_until_iterator_2() { + let res = iterator(&b"2017-01-01 every 2mins until 2017-01-01T00:10:00"[..]); + assert!(res.is_done(), format!("Not done: {:?}", res)); + let (_, i) = res.unwrap(); + println!("{:#?}", i); + + let ui : Result, _> = i.into_user_iterator(); + assert!(ui.is_ok(), "Not okay: {:#?}", ui); + let mut ui = ui.unwrap(); + + for min in (1..60).into_iter().filter(|n| n % 2 == 0) { + if min > 10 - 1 { + let n = ui.next(); + assert!(n.is_none(), "Is Some, should be None: {:?}", n); + return; + } else { + let n = ui.next().unwrap(); + assert!(n.is_ok(), "Not ok: {:#?}", n); + let tt = n.unwrap(); + assert_eq!(tt.get_moment().unwrap().year() , 2017); + assert_eq!(tt.get_moment().unwrap().month() , 01); + assert_eq!(tt.get_moment().unwrap().day() , 01); + assert_eq!(tt.get_moment().unwrap().hour() , 00); + assert_eq!(tt.get_moment().unwrap().minute(), min); + assert_eq!(tt.get_moment().unwrap().second(), 00); + } + } + } + + #[test] + fn test_until_iterator_3() { + let res = iterator(&b"2017-01-01 daily until 2017-01-05"[..]); + assert!(res.is_done(), format!("Not done: {:?}", res)); + let (_, i) = res.unwrap(); + println!("{:#?}", i); + + let ui : Result, _> = i.into_user_iterator(); + assert!(ui.is_ok(), "Not okay: {:#?}", ui); + let mut ui = ui.unwrap(); + + for day in 2..30 { + if day > 5 - 1 { + let n = ui.next(); + assert!(n.is_none(), "Is Some, should be None: {:?}", n); + return; + } else { + let n = ui.next().unwrap(); + assert!(n.is_ok(), "Not ok: {:#?}", n); + let tt = n.unwrap(); + assert_eq!(tt.get_moment().unwrap().year() , 2017); + assert_eq!(tt.get_moment().unwrap().month() , 01); + assert_eq!(tt.get_moment().unwrap().day() , day); + assert_eq!(tt.get_moment().unwrap().hour() , 00); + assert_eq!(tt.get_moment().unwrap().minute(), 00); + assert_eq!(tt.get_moment().unwrap().second(), 00); + } + } + } + + #[test] + fn test_until_iterator_4() { + let res = iterator(&b"2017-01-01 weekly until 2017-01-14"[..]); + assert!(res.is_done(), format!("Not done: {:?}", res)); + let (_, i) = res.unwrap(); + println!("{:#?}", i); + + let ui : Result, _> = i.into_user_iterator(); + assert!(ui.is_ok(), "Not okay: {:#?}", ui); + let mut ui = ui.unwrap(); + + for week in 1..3 { + if (week * 7 + 1) > 14 { + let n = ui.next(); + assert!(n.is_none(), "Is Some, should be None: {:?}", n); + return; + } else { + let n = ui.next().unwrap(); + assert!(n.is_ok(), "Not ok: {:#?}", n); + let tt = n.unwrap(); + assert_eq!(tt.get_moment().unwrap().year() , 2017); + assert_eq!(tt.get_moment().unwrap().month() , 01); + assert_eq!(tt.get_moment().unwrap().day() , 01 + (week * 7)); + assert_eq!(tt.get_moment().unwrap().hour() , 00); + assert_eq!(tt.get_moment().unwrap().minute(), 00); + assert_eq!(tt.get_moment().unwrap().second(), 00); + } + } + } } -- cgit v1.2.3 From b0dddb1a10c5c2c64d945e4991b99dd2bc699bf5 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 19 Nov 2017 13:43:18 +0100 Subject: Add Known Problems to README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 0a5d012..b064e18 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,11 @@ Plus, we want to offer a string-parser which can be used to parse user input into such things. This will be a compiletime option to include the parser or not. +# Known Problems + +* Iterators such as "today daily" start one day off. So "today daily" yields + tomorrow as first item. + # License MPL 2.0 -- cgit v1.2.3