From 8520fe52226d3586f9b4d7ece968f9bbf6450d0c Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 26 Sep 2017 17:03:17 +0200 Subject: Add IntoCalculatingIter trait for calling calculate() on all iterators --- src/iter.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/iter.rs b/src/iter.rs index 4bfc2ca..821c9db 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -47,10 +47,6 @@ impl Iter { self.base -= self.increment.clone(); } - pub fn calculate(self) -> CalculatingIter { - CalculatingIter::new(self) - } - } /// # Warning @@ -78,13 +74,17 @@ impl Iterator for Iter { pub struct CalculatingIter(I) where I: Iterator; -impl> CalculatingIter { +impl CalculatingIter + where I: Iterator +{ pub fn new(i: I) -> CalculatingIter { CalculatingIter(i) } } -impl> Iterator for CalculatingIter { +impl Iterator for CalculatingIter + where I: Iterator +{ type Item = Result; fn next(&mut self) -> Option { @@ -93,6 +93,18 @@ impl> Iterator for CalculatingIter { } +pub trait IntoCalculatingIter : Iterator + Sized { + fn calculate(self) -> CalculatingIter; +} + +impl IntoCalculatingIter for I + where I: Iterator +{ + fn calculate(self) -> CalculatingIter { + CalculatingIter(self) + } +} + pub mod extensions { use timetype::TimeType as TT; use super::Iter; @@ -234,6 +246,7 @@ pub mod extensions { use super::*; use timetype::TimeType as TT; use chrono::NaiveDate as ND; + use iter::IntoCalculatingIter; fn ymd_hms(y: i32, m: u32, d: u32, h: u32, mi: u32, s: u32) -> TT { TT::moment(ND::from_ymd(y, m, d).and_hms(h, mi, s)) -- cgit v1.2.3 From 694a9a4ecb4f743ab7958a66a042381a812ad156 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 26 Sep 2017 17:18:03 +0200 Subject: Add Matcher trait for indicators --- src/error.rs | 5 +++++ src/lib.rs | 1 + src/matcher.rs | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 src/matcher.rs diff --git a/src/error.rs b/src/error.rs index d450146..39d482f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,6 +33,11 @@ error_chain! { display("The passed argument is not an amount: {:?}", tt) } + ArgumentErrorNotAMoment(name: &'static str) { + description("Argument Error: Not a moment TimeType object") + display("The passed argument is not a moment, but a {}", name) + } + CannotCalculateEndOfYearOn(tt: TimeType) { description("Argument Error: Cannot calculate end-of-year") display("Argument Error: Cannot calculate end-of-year on a {:?}", tt) diff --git a/src/lib.rs b/src/lib.rs index 60bcfc1..e861ccd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,5 +7,6 @@ pub mod iter; pub mod result; pub mod timetype; pub mod indicator; +pub mod matcher; mod util; diff --git a/src/matcher.rs b/src/matcher.rs new file mode 100644 index 0000000..1482b68 --- /dev/null +++ b/src/matcher.rs @@ -0,0 +1,37 @@ + +use chrono::Datelike; + +use error::KairosError as KE; +use error::KairosErrorKind as KEK; +use error::Result; +use indicator::Day; +use indicator::Month; +use timetype::TimeType; + +/// A trait to extend indicator::* to be able to match them with a TimeType object +pub trait Matcher { + fn matches(&self, tt: &TimeType) -> Result; +} + +impl Matcher for Day { + + fn matches(&self, tt: &TimeType) -> Result { + let this : ::chrono::Weekday = self.clone().into(); + tt.get_moment() + .map(|mom| this == mom.weekday()) + .ok_or(KE::from_kind(KEK::ArgumentErrorNotAMoment(tt.name()))) + } +} + +impl Matcher for Month { + + fn matches(&self, tt: &TimeType) -> Result { + let this : u32 = self.clone().into(); + tt.get_moment() + .map(|mom| this == mom.month()) + .ok_or(KE::from_kind(KEK::ArgumentErrorNotAMoment(tt.name()))) + } + +} + + -- cgit v1.2.3 From 77c3f75343dc84b87e99e333bc15ea5c308fc017 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 26 Sep 2017 17:18:35 +0200 Subject: Add filter iterator --- src/iter.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/iter.rs b/src/iter.rs index 821c9db..7e53da4 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -7,6 +7,7 @@ use error::KairosError as KE; use error::KairosErrorKind as KEK; use error::Result; use timetype::TimeType; +use matcher::Matcher; pub struct Iter { base: TimeType, @@ -105,6 +106,53 @@ impl IntoCalculatingIter for I } } +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, M) -> FilterIter; +} + +impl EveryFilter for I + where I: Iterator>, + M: Matcher +{ + fn every(self, matcher: M) -> FilterIter { + FilterIter::new(self, matcher) + } +} + pub mod extensions { use timetype::TimeType as TT; use super::Iter; @@ -335,3 +383,32 @@ pub mod extensions { } } + +#[cfg(test)] +mod type_tests { + use super::*; + use super::IntoCalculatingIter; + use super::extensions::*; + + #[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() + .calculate() + .every(::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() + .calculate() + .every(::indicator::Day::Monday) + .every(::indicator::Month::January); + } +} + -- cgit v1.2.3 From 4be0e3b361615a84ecf1402b827a81ca62e95bc5 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 26 Sep 2017 17:41:20 +0200 Subject: Implement filter interface for matchers --- Cargo.toml | 7 +++++++ src/indicator.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ src/iter.rs | 17 +++++++++++++++++ src/lib.rs | 3 +++ src/matcher.rs | 11 +++++++++++ 5 files changed, 80 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 17b7e02..4995361 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,10 @@ repository = "https://github.com/matthiasbeyer/kairos" [dependencies] chrono = "0.4" error-chain = "0.10" + +filters = { version = "0.1.1", optional = true } + +[features] +default = [] +with-filters = [ "filters" ] + diff --git a/src/indicator.rs b/src/indicator.rs index 7a57b7e..72e1136 100644 --- a/src/indicator.rs +++ b/src/indicator.rs @@ -57,3 +57,45 @@ impl Into for Month { } } } + +#[cfg(feature = "with-filters")] +pub struct DayFilter(Day); + +#[cfg(feature = "with-filters")] +impl Filter for DayFilter { + fn filter(&self, tt: &TimeType) -> bool { + tt.get_moment(|mom| mom.weekday() == self.0.into()).unwrap_or(false) + } +} + +#[cfg(feature = "with-filters")] +impl IntoFilter for Day { + type IntoFilt = DayFilter; + + fn into_filter(self) -> Self::IntoFilt { + DayFilter(self) + } + +} + + +#[cfg(feature = "with-filters")] +pub struct MonthFilter(Month); + +#[cfg(feature = "with-filters")] +impl Filter for MonthFilter { + fn filter(&self, tt: &TimeType) -> bool { + tt.get_moment(|mom| mom.month() == self.0.into()).unwrap_or(false) + } +} + +#[cfg(feature = "with-filters")] +impl IntoFilter for Month { + type IntoFilt = MonthFilter; + + fn into_filter(self) -> Self::IntoFilt { + MonthFilter(self) + } + +} + diff --git a/src/iter.rs b/src/iter.rs index 7e53da4..0b8c6a0 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -412,3 +412,20 @@ mod type_tests { } } +#[cfg(all(feature = "with-filters", test))] +mod type_tests_filter_interface { + use super::*; + use super::IntoCalculatingIter; + use super::extensions::*; + + #[test] + fn test_compile() { + // This test is solely to check whether this compiles and the API is nice + let _ = TimeType::today() + .yearly(1) + .unwrap() + .calculate() + .every(::indicator::Day::Monday.or(::indicator::Month::January)); + } +} + diff --git a/src/lib.rs b/src/lib.rs index e861ccd..b537b14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,9 @@ extern crate error_chain; extern crate chrono; +#[cfg(feature = "with-filters")] +extern crate filters; + pub mod error; pub mod iter; pub mod result; diff --git a/src/matcher.rs b/src/matcher.rs index 1482b68..d574090 100644 --- a/src/matcher.rs +++ b/src/matcher.rs @@ -34,4 +34,15 @@ impl Matcher for Month { } +#[cfg(feature = "with-filters")] +use filters::filter::*; + +#[cfg(feature = "with-filters")] +impl Matcher for F + where F: Filter +{ + fn matches(&self, tt: &TimeType) -> Result { + Ok(self.filter(tt)) + } +} -- cgit v1.2.3