diff options
author | Matthias Beyer <mail@beyermatthias.de> | 2017-11-19 15:11:44 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-19 15:11:44 +0100 |
commit | 633e1a707da7bc93191c7dfd8090a5ed91a19607 (patch) | |
tree | 4a23694727212d2cac0f1cf7c084129a75f7f27d | |
parent | 22fd8afc01a914d70fc62b34ec9a39680a93a4e9 (diff) | |
parent | 95e07f51a2cc3e04f356d15497da7278d75523fd (diff) |
Merge pull request #7 from matthiasbeyer/parser-interface
Parser interface
-rw-r--r-- | examples/main.rs | 26 | ||||
-rw-r--r-- | src/parser/iterator.rs | 396 | ||||
-rw-r--r-- | src/parser/mod.rs | 77 | ||||
-rw-r--r-- | src/parser/timetype.rs (renamed from src/parser.rs) | 465 | ||||
-rw-r--r-- | src/timetype.rs | 8 |
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 |