summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2017-11-19 15:11:44 +0100
committerGitHub <noreply@github.com>2017-11-19 15:11:44 +0100
commit633e1a707da7bc93191c7dfd8090a5ed91a19607 (patch)
tree4a23694727212d2cac0f1cf7c084129a75f7f27d
parent22fd8afc01a914d70fc62b34ec9a39680a93a4e9 (diff)
parent95e07f51a2cc3e04f356d15497da7278d75523fd (diff)
Merge pull request #7 from matthiasbeyer/parser-interface
Parser interface
-rw-r--r--examples/main.rs26
-rw-r--r--src/parser/iterator.rs396
-rw-r--r--src/parser/mod.rs77
-rw-r--r--src/parser/timetype.rs (renamed from src/parser.rs)465
-rw-r--r--src/timetype.rs8
5 files changed, 517 insertions, 455 deletions
diff --git a/examples/main.rs b/examples/main.rs
index 920accc..3c301cf 100644
--- a/examples/main.rs
+++ b/examples/main.rs
@@ -6,15 +6,31 @@ fn main() {
let s = s.trim(); // because kairos is not yet whitespace tolerant
println!("Parsing: '{}'", s);
- match kairos::timetype::TimeType::parse(&s) {
- Ok(tt) => {
- println!("{:?}", tt);
+ match kairos::parser::parse(s) {
+ Err(e) => println!("Error -> {:?}", e),
+ Ok(kairos::parser::Parsed::TimeType(tt)) => {
+ println!("Having TimeType");
match tt.calculate() {
- Ok(r) => println!("{:?}", r),
+ Ok(r) => println!("{:?}", r),
Err(e) => println!("Error calculating: {:?}", e),
}
},
- Err(e) => println!("Error -> {:?}", e),
+ Ok(kairos::parser::Parsed::Iterator(Ok(ui))) => {
+ println!("Having iterator");
+
+ for elem in ui {
+ match elem {
+ Ok(r) => println!("{:?}", r),
+ Err(e) => {
+ println!("Error calculating: {:?}", e);
+ ::std::process::exit(1)
+ }
+ }
+ }
+ },
+ Ok(kairos::parser::Parsed::Iterator(Err(e))) => {
+ println!("Failed building iterator: {:?}", e);
+ },
}
}
diff --git a/src/parser/iterator.rs b/src/parser/iterator.rs
new file mode 100644
index 0000000..f86e98f
--- /dev/null
+++ b/src/parser/iterator.rs
@@ -0,0 +1,396 @@
+use std::str;
+use std::str::FromStr;
+
+use nom::{IResult, space, alpha, alphanumeric, digit};
+use nom::whitespace::sp;
+use chrono::NaiveDate;
+
+use parser::timetype::*;
+use timetype;
+use iter;
+use error;
+
+named!(pub iter_spec<Iterspec>, alt_complete!(
+ tag!("secondly") => { |_| Iterspec::Secondly } |
+ tag!("minutely") => { |_| Iterspec::Minutely } |
+ tag!("hourly") => { |_| Iterspec::Hourly } |
+ tag!("daily") => { |_| Iterspec::Daily } |
+ tag!("weekly") => { |_| Iterspec::Weekly } |
+ tag!("monthly") => { |_| Iterspec::Monthly } |
+ tag!("yearly") => { |_| Iterspec::Yearly } |
+ do_parse!(
+ tag!("every") >>
+ number:integer >>
+ unit:unit_parser >>
+ (Iterspec::Every(number, unit))
+ )
+));
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum Iterspec {
+ Secondly,
+ Minutely,
+ Hourly,
+ Daily,
+ Weekly,
+ Monthly,
+ Yearly,
+ Every(i64, Unit),
+}
+
+named!(pub until_spec<UntilSpec>, alt_complete!(
+ do_parse!(
+ tag!("until") >> sp >>
+ exact: exact_date_parser >>
+ (UntilSpec::Exact(exact))
+ ) |
+ do_parse!(
+ num: integer >> sp >>
+ tag!("times") >>
+ (UntilSpec::Times(num))
+ )
+));
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum UntilSpec {
+ Exact(ExactDate),
+ Times(i64)
+}
+
+named!(pub iterator<Iterator>, do_parse!(
+ 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 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(),
+ }
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use nom::IResult;
+ use super::*;
+
+ use chrono::Timelike;
+ use chrono::Datelike;
+
+ #[test]
+ fn test_iterspec() {
+ assert_eq!(iter_spec(&b"secondly"[..]), IResult::Done(&b""[..], Iterspec::Secondly));
+ assert_eq!(iter_spec(&b"minutely"[..]), IResult::Done(&b""[..], Iterspec::Minutely));
+ assert_eq!(iter_spec(&b"hourly"[..]), IResult::Done(&b""[..], Iterspec::Hourly));
+ assert_eq!(iter_spec(&b"daily"[..]), IResult::Done(&b""[..], Iterspec::Daily));
+ assert_eq!(iter_spec(&b"weekly"[..]), IResult::Done(&b""[..], Iterspec::Weekly));
+ assert_eq!(iter_spec(&b"monthly"[..]), IResult::Done(&b""[..], Iterspec::Monthly));
+ assert_eq!(iter_spec(&b"yearly"[..]), IResult::Done(&b""[..], Iterspec::Yearly));
+ assert_eq!(iter_spec(&b"every 5min"[..]), IResult::Done(&b""[..], Iterspec::Every(5, Unit::Minute)));
+ }
+
+ #[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 0..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 (0..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 1..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 0..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 0..10 { // 10 is randomly chosen (fair dice roll... )
+ if hour > 4 {
+ 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 (0..60).into_iter().filter(|n| n % 2 == 0) {
+ if min > 9 {
+ 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 1..30 {
+ if day > 4 {
+ 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 0..3 {
+ if (week * 7) > 13 {
+ 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);
+ }
+ }
+ }
+}
+
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
new file mode 100644
index 0000000..3c01903
--- /dev/null
+++ b/src/parser/mod.rs
@@ -0,0 +1,77 @@
+//! The definition of the "kairos" syntax, for parsing user input into TimeType objects
+//!
+//! The syntax itself is described in the grammar.rustpeg file.
+//! Here goes a documentation on the syntax
+//!
+//! # Syntax
+//!
+//! ## Units
+//!
+//! UnitSec = "second" | "seconds" | "sec" | "secs" | "s"
+//! UnitMin = "minute" | "minutes" | "min" | "mins"
+//! UnitHr = "hour" | "hours" | "hr" | "hrs"
+//! UnitDay = "day" | "days" | "d"
+//! UnitWeek = "week" | "weeks" | "w"
+//! UnitMonth = "month" | "months" |
+//! UnitYear = "year" | "years" | "yrs"
+//! Unit = UnitSec | UnitMin | UnitHr | UnitDay | UnitWeek | UnitMonth | UnitYear
+//!
+//! ## Operators
+//!
+//! Operator = "+" | "-"
+//!
+//! ## Intermediate syntax nodes
+//!
+//! Amount = "<Number><Unit>"
+//!
+//! TextIterSpec = "secondly" | "minutely" | "hourly" | "daily" | "weekly" | "monthly" | "yearly"
+//! Iterspec = TextIterSpec | "every" <Number><Unit>
+//!
+//! ## User-facing syntax nodes
+//!
+//! AmountExpr = <Amount> (<Operator> <AmountExpr>)?
+//! ExactDate = "today" | "yesterday" | "tomorrow" | <Iso8601>
+//! Date = <ExactDate> (<Operator> <AmountExpr>)?
+//! Iterator = <Date> <Iterspec> ("until" <ExactDate> | <number> "times")?
+//!
+//! # Warning
+//!
+//! This module is not intended for public use... it is still public, so you can use it, but you
+//! should know that these interfaces are considered private and I will not follow semver and
+//! update the minor or major semver numbers of the interface of this module changes.
+//!
+//! Be warned!
+//!
+
+use nom::Needed;
+use nom::IResult;
+
+mod timetype;
+mod iterator;
+
+use error::Result;
+use error::KairosErrorKind as KEK;
+use iter::Iter;
+use parser::timetype::timetype;
+use parser::iterator::iterator;
+
+pub enum Parsed {
+ Iterator(Result<::parser::iterator::UserIterator<Iter>>),
+ TimeType(::timetype::TimeType)
+}
+
+named!(do_parse<Parsed>, alt_complete!(
+ do_parse!(it: iterator >> (Parsed::Iterator(it.into_user_iterator()))) |
+ do_parse!(tt: timetype >> (Parsed::TimeType(tt.into())))
+));
+
+pub fn parse(s: &str) -> Result<Parsed> {
+ match do_parse(s.as_bytes()) {
+ IResult::Done(_, o) => Ok(o),
+ IResult::Error(e) => Err(e).map_err(From::from),
+ IResult::Incomplete(Needed::Unknown) => Err(KEK::UnknownParserError.into()),
+ IResult::Incomplete(Needed::Size(s)) => Err(KEK::UnknownParserError.into()),
+
+ }
+}
+
diff --git a/src/parser.rs b/src/parser/timetype.rs
index 5d518f5..bf2422a 100644
--- a/src/parser.rs
+++ b/src/parser/timetype.rs
@@ -1,59 +1,15 @@
-//! The definition of the "kairos" syntax, for parsing user input into TimeType objects
-//!
-//! The syntax itself is described in the grammar.rustpeg file.
-//! Here goes a documentation on the syntax
-//!
-//! # Syntax
-//!
-//! ## Units
-//!
-//! UnitSec = "second" | "seconds" | "sec" | "secs" | "s"
-//! UnitMin = "minute" | "minutes" | "min" | "mins"
-//! UnitHr = "hour" | "hours" | "hr" | "hrs"
-//! UnitDay = "day" | "days" | "d"
-//! UnitWeek = "week" | "weeks" | "w"
-//! UnitMonth = "month" | "months" |
-//! UnitYear = "year" | "years" | "yrs"
-//! Unit = UnitSec | UnitMin | UnitHr | UnitDay | UnitWeek | UnitMonth | UnitYear
-//!
-//! ## Operators
-//!
-//! Operator = "+" | "-"
-//!
-//! ## Intermediate syntax nodes
-//!
-//! Amount = "<Number><Unit>"
-//!
-//! TextIterSpec = "secondly" | "minutely" | "hourly" | "daily" | "weekly" | "monthly" | "yearly"
-//! Iterspec = TextIterSpec | "every" <Number><Unit>
-//!
-//! ## User-facing syntax nodes
-//!
-//! AmountExpr = <Amount> (<Operator> <AmountExpr>)?
-//! ExactDate = "today" | "yesterday" | "tomorrow" | <Iso8601>
-//! Date = <ExactDate> (<Operator> <AmountExpr>)?
-//! Iterator = <Date> <Iterspec> ("until" <ExactDate> | <number> "times")?
-//!
-//! # Warning
-//!
-//! This module is not intended for public use... it is still public, so you can use it, but you
-//! should know that these interfaces are considered private and I will not follow semver and
-//! update the minor or major semver numbers of the interface of this module changes.
-//!
-//! Be warned!
-//!
-
-use nom::{IResult, space, alpha, alphanumeric, digit};
use std::str;
use std::str::FromStr;
+use nom::{IResult, space, alpha, alphanumeric, digit};
+use nom::whitespace::sp;
use chrono::NaiveDate;
use timetype;
use iter;
use error;
-named!(integer<i64>, alt!(
+named!(pub integer<i64>, alt!(
map_res!(
map_res!(
ws!(digit),
@@ -64,7 +20,7 @@ named!(integer<i64>, alt!(
));
// WARNING: Order is important here. Long tags first, shorter tags later
-named!(unit_parser<Unit>, alt_complete!(
+named!(pub unit_parser<Unit>, alt_complete!(
tag!("seconds") => { |_| Unit::Second } |
tag!("second") => { |_| Unit::Second } |
tag!("secs") => { |_| Unit::Second } |
@@ -102,7 +58,7 @@ pub enum Unit {
Year,
}
-named!(operator_parser<Operator>, alt!(
+named!(pub operator_parser<Operator>, alt!(
tag!("+") => { |_| Operator::Plus } |
tag!("-") => { |_| Operator::Minus }
));
@@ -113,7 +69,7 @@ pub enum Operator {
Minus,
}
-named!(amount_parser<Amount>, do_parse!(
+named!(pub amount_parser<Amount>, do_parse!(
number: integer >>
unit : unit_parser >>
(Amount(number, unit))
@@ -136,44 +92,14 @@ impl Into<timetype::TimeType> for Amount {
}
}
-named!(iter_spec<Iterspec>, alt_complete!(
- tag!("secondly") => { |_| Iterspec::Secondly } |
- tag!("minutely") => { |_| Iterspec::Minutely } |
- tag!("hourly") => { |_| Iterspec::Hourly } |
- tag!("daily") => { |_| Iterspec::Daily } |
- tag!("weekly") => { |_| Iterspec::Weekly } |
- tag!("monthly") => { |_| Iterspec::Monthly } |
- tag!("yearly") => { |_| Iterspec::Yearly } |
- do_parse!(
- tag!("every") >>
- number:integer >>
- unit:unit_parser >>
- (Iterspec::Every(number, unit))
- )
-));
-
-#[derive(Debug, PartialEq, Eq)]
-pub enum Iterspec {
- Secondly,
- Minutely,
- Hourly,
- Daily,
- Weekly,
- Monthly,
- Yearly,
- Every(i64, Unit),
-}
-
-use nom::whitespace::sp;
-
-named!(amount_expr_next<(Operator, Box<AmountExpr>)>, do_parse!(
+named!(pub amount_expr_next<(Operator, Box<AmountExpr>)>, do_parse!(
op:operator_parser
>> opt!(sp)
>> amexp:amount_expr
>> ((op, Box::new(amexp)))
));
-named!(amount_expr<AmountExpr>, do_parse!(
+named!(pub amount_expr<AmountExpr>, do_parse!(
amount:amount_parser >>
opt!(sp) >>
o: opt!(complete!(amount_expr_next)) >>
@@ -218,7 +144,7 @@ use iso8601::parsers::parse_date;
use iso8601::parsers::parse_datetime;
// The order is relevant here, because datetime is longer than date, we must parse datetime before
// date.
-named!(exact_date_parser<ExactDate>, alt_complete!(
+named!(pub exact_date_parser<ExactDate>, alt_complete!(
tag!("today") => { |_| ExactDate::Today } |
tag!("yesterday") => { |_| ExactDate::Yesterday } |
tag!("tomorrow") => { |_| ExactDate::Tomorrow } |
@@ -278,7 +204,7 @@ impl Into<timetype::TimeType> for ExactDate {
}
}
-named!(date<Date>, do_parse!(
+named!(pub date<Date>, do_parse!(
exact: exact_date_parser >>
o: opt!(
complete!(do_parse!(sp >> op:operator_parser >> sp >> a:amount_expr >> (op, a)))
@@ -300,18 +226,6 @@ impl Into<timetype::TimeType> for Date {
}
}
-/// Main entry function for timetype parser
-///
-/// # Notice
-///
-/// Note that this function returns a parser::TimeType, not a timetype::TimeType. Though, the
-/// parser::TimeType can be `Into::into()`ed.
-///
-named!(pub timetype<TimeType>, alt!(
- do_parse!(d: date >> (TimeType::Date(d))) |
- do_parse!(a: amount_expr >> (TimeType::AmountExpr(a)))
-));
-
#[derive(Debug, PartialEq, Eq)]
pub enum TimeType {
Date(Date),
@@ -327,115 +241,19 @@ impl Into<timetype::TimeType> for TimeType {
}
}
-named!(until_spec<UntilSpec>, alt_complete!(
- do_parse!(
- tag!("until") >> sp >>
- exact: exact_date_parser >>
- (UntilSpec::Exact(exact))
- ) |
- do_parse!(
- num: integer >> sp >>
- tag!("times") >>
- (UntilSpec::Times(num))
- )
-));
-
-#[derive(Debug, PartialEq, Eq)]
-pub enum UntilSpec {
- Exact(ExactDate),
- Times(i64)
-}
-named!(iterator<Iterator>, do_parse!(
- opt!(sp) >> d: date >>
- opt!(sp) >> spec: iter_spec >>
- opt!(sp) >> until: opt!(complete!(until_spec)) >>
- (Iterator(d, spec, until))
+/// Main entry function for timetype parser
+///
+/// # Notice
+///
+/// Note that this function returns a parser::TimeType, not a timetype::TimeType. Though, the
+/// parser::TimeType can be `Into::into()`ed.
+///
+named!(pub timetype<TimeType>, alt!(
+ do_parse!(d: date >> (TimeType::Date(d))) |
+ do_parse!(a: amount_expr >> (TimeType::AmountExpr(a)))
));
-#[derive(Debug, PartialEq, Eq)]
-pub struct Iterator(Date, Iterspec, Option<UntilSpec>);
-
-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(),
- }
- }
-}
-
#[cfg(test)]
mod tests {
use nom::IResult;
@@ -494,18 +312,6 @@ mod tests {
}
#[test]
- fn test_iterspec() {
- assert_eq!(iter_spec(&b"secondly"[..]), IResult::Done(&b""[..], Iterspec::Secondly));
- assert_eq!(iter_spec(&b"minutely"[..]), IResult::Done(&b""[..], Iterspec::Minutely));
- assert_eq!(iter_spec(&b"hourly"[..]), IResult::Done(&b""[..], Iterspec::Hourly));
- assert_eq!(iter_spec(&b"daily"[..]), IResult::Done(&b""[..], Iterspec::Daily));
- assert_eq!(iter_spec(&b"weekly"[..]), IResult::Done(&b""[..], Iterspec::Weekly));
- assert_eq!(iter_spec(&b"monthly"[..]), IResult::Done(&b""[..], Iterspec::Monthly));
- assert_eq!(iter_spec(&b"yearly"[..]), IResult::Done(&b""[..], Iterspec::Yearly));
- assert_eq!(iter_spec(&b"every 5min"[..]), IResult::Done(&b""[..], Iterspec::Every(5, Unit::Minute)));
- }
-
- #[test]
fn test_amountexpr_next() {
assert_eq!(amount_expr_next(&b"+ 12minutes"[..]),
IResult::Done(&b""[..],
@@ -724,7 +530,7 @@ mod tests {
assert!(res.is_done());
let (_, o) = res.unwrap();
- let calc_res : timetype::TimeType = o.into();
+ let calc_res : ::timetype::TimeType = o.into();
let calc_res = calc_res.calculate();
assert!(calc_res.is_ok());
@@ -742,7 +548,7 @@ mod tests {
assert!(res.is_done(), "Not done: {:?}", res.unwrap_err().description());
let (_, o) = res.unwrap();
- let calc_res : timetype::TimeType = o.into();
+ let calc_res : ::timetype::TimeType = o.into();
let calc_res = calc_res.calculate();
assert!(calc_res.is_ok());
@@ -759,7 +565,7 @@ mod tests {
println!("{:#?}", o);
- let calc_res : timetype::TimeType = o.into();
+ let calc_res : ::timetype::TimeType = o.into();
println!("{:#?}", calc_res);
let calc_res = calc_res.calculate();
@@ -774,229 +580,4 @@ 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 0..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 (0..59).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 1..29 {
- 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 0..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);
- asse