summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2017-11-19 14:46:03 +0100
committerGitHub <noreply@github.com>2017-11-19 14:46:03 +0100
commit27d98f1416b879438e185368c7a1e42d1162338d (patch)
tree1e755cdf998b4aa69d74ada20c5461e500ba714b
parent11a71c0f00d22b8ab3d42ad9429ba676f193d650 (diff)
parentb0dddb1a10c5c2c64d945e4991b99dd2bc699bf5 (diff)
Merge pull request #5 from matthiasbeyer/parse-iter
Parse iter
-rw-r--r--README.md5
-rw-r--r--src/error.rs5
-rw-r--r--src/iter.rs88
-rw-r--r--src/parser.rs312
4 files changed, 404 insertions, 6 deletions
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
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")
diff --git a/src/iter.rs b/src/iter.rs
index 64c6ba1..b253f9f 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>(I, M)
where I: Iterator<Item = Result<TimeType>>,
M: Matcher;
@@ -127,6 +129,7 @@ impl<I, M> EveryFilter<M> for I
}
}
+#[derive(Debug)]
pub struct WithoutIter<I, M>(I, M)
where I: Iterator<Item = Result<TimeType>>,
M: Matcher;
@@ -174,6 +177,7 @@ impl<I, M> WithoutFilter<M> for I
}
}
+#[derive(Debug)]
pub struct UntilIter<I>(I, NaiveDateTime)
where I: Iterator<Item = Result<TimeType>>;
@@ -222,6 +226,54 @@ impl<I> Until for I
}
}
+#[derive(Debug)]
+pub struct TimesIter<I>
+ where I: Iterator<Item = Result<TimeType>>
+{
+ inner: I,
+ times: i64,
+ count: i64,
+}
+
+impl<I> TimesIter<I>
+ where I: Iterator<Item = Result<TimeType>>
+{
+ fn new(i: I, times: i64) -> TimesIter<I> {
+ TimesIter {
+ inner: i,
+ times: times,
+ count: 0,
+ }
+ }
+}
+
+impl<I> Iterator for TimesIter<I>
+ where I: Iterator<Item = Result<TimeType>>
+{
+ type Item = Result<TimeType>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.times == self.count {
+ None
+ } else {
+ self.count += 1;
+ self.inner.next()
+ }
+ }
+}
+
+pub trait Times : Iterator<Item = Result<TimeType>> + Sized {
+ fn times(self, i64) -> TimesIter<Self>;
+}
+
+impl<I> Times for I
+ where I: Iterator<Item = Result<TimeType>>
+{
+ fn times(self, times: i64) -> TimesIter<Self> {
+ TimesIter::new(self, times)
+ }
+}
+
pub mod extensions {
use timetype::TimeType as TT;
use super::Iter;
@@ -528,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::<Vec<_>>();
+
+ 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::<Vec<_>>();
+
+ assert_eq!(v.len(), 48 - 1); // -1 because we do not start to iterate with 0, but 1
+ }
+
}
diff --git a/src/parser.rs b/src/parser.rs
index 7a2e7a1..723f4f3 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<i64>, alt!(
map_res!(
@@ -345,18 +347,92 @@ pub enum UntilSpec {
}
named!(iterator<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<UntilSpec>);
-impl Into<timetype::TimeType> for Iterator {
- fn into(self) -> timetype::TimeType {
- unimplemented!()
+impl Iterator {
+ pub fn into_user_iterator(self) -> error::Result<UserIterator<iter::Iter>> {
+ 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<I>
+ where I: ::std::iter::Iterator<Item = error::Result<timetype::TimeType>>
+{
+ Iterator(iter::Iter),
+ TimesIter(iter::TimesIter<I>),
+ UntilIterator(iter::UntilIter<I>)
+}
+
+impl<I> ::std::iter::Iterator for UserIterator<I>
+ where I: ::std::iter::Iterator<Item = error::Result<timetype::TimeType>>
+{
+ type Item = error::Result<timetype::TimeType>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match *self {
+ UserIterator::Iterator(ref mut i) => i.next(),
+ UserIterator::TimesIter(ref mut i) => i.next(),
+ UserIterator::UntilIterator(ref mut i) => i.next(),
+ }
}
}
@@ -698,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<UserIterator<iter::Iter>, _> = 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<UserIterator<iter::Iter>, _> = 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<UserIterator<iter::Iter>, _> = 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<UserIterator<iter::Iter>, _> = 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<UserIterator<iter::Iter>, _> = 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<UserIterator<iter::Iter>, _> = 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<UserIterator<iter::Iter>, _> = 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<UserIterator<iter::Iter>, _> = 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);
+ }
+ }
+ }
}