diff options
author | Matthias Beyer <mail@beyermatthias.de> | 2017-09-26 17:58:38 +0200 |
---|---|---|
committer | Matthias Beyer <mail@beyermatthias.de> | 2017-09-26 17:58:38 +0200 |
commit | 874fcc34d50d6d69e30cc3ce6ef14e617354fe4c (patch) | |
tree | 900ca070850269155815499b88307566cecd0a50 | |
parent | 79923c0cd6ed917b3b0463a66d0a44df85165e23 (diff) | |
parent | 4be0e3b361615a84ecf1402b827a81ca62e95bc5 (diff) |
Merge branch 'more-iter'
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | src/error.rs | 5 | ||||
-rw-r--r-- | src/indicator.rs | 42 | ||||
-rw-r--r-- | src/iter.rs | 119 | ||||
-rw-r--r-- | src/lib.rs | 4 | ||||
-rw-r--r-- | src/matcher.rs | 48 |
6 files changed, 219 insertions, 6 deletions
@@ -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/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/indicator.rs b/src/indicator.rs index 7a57b7e..72e1136 100644 --- a/src/indicator.rs +++ b/src/indicator.rs @@ -57,3 +57,45 @@ impl Into<u32> for Month { } } } + +#[cfg(feature = "with-filters")] +pub struct DayFilter(Day); + +#[cfg(feature = "with-filters")] +impl Filter<TimeType> 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<TimeType> 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<TimeType> 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<TimeType> for Month { + type IntoFilt = MonthFilter; + + fn into_filter(self) -> Self::IntoFilt { + MonthFilter(self) + } + +} + diff --git a/src/iter.rs b/src/iter.rs index 4bfc2ca..0b8c6a0 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, @@ -47,10 +48,6 @@ impl Iter { self.base -= self.increment.clone(); } - pub fn calculate(self) -> CalculatingIter<Self> { - CalculatingIter::new(self) - } - } /// # Warning @@ -78,13 +75,17 @@ impl Iterator for Iter { pub struct CalculatingIter<I>(I) where I: Iterator<Item = TimeType>; -impl<I: Iterator<Item = TimeType>> CalculatingIter<I> { +impl<I> CalculatingIter<I> + where I: Iterator<Item = TimeType> +{ pub fn new(i: I) -> CalculatingIter<I> { CalculatingIter(i) } } -impl<I: Iterator<Item = TimeType>> Iterator for CalculatingIter<I> { +impl<I> Iterator for CalculatingIter<I> + where I: Iterator<Item = TimeType> +{ type Item = Result<TimeType>; fn next(&mut self) -> Option<Self::Item> { @@ -93,6 +94,65 @@ impl<I: Iterator<Item = TimeType>> Iterator for CalculatingIter<I> { } +pub trait IntoCalculatingIter : Iterator<Item = TimeType> + Sized { + fn calculate(self) -> CalculatingIter<Self>; +} + +impl<I> IntoCalculatingIter for I + where I: Iterator<Item = TimeType> +{ + fn calculate(self) -> CalculatingIter<Self> { + CalculatingIter(self) + } +} + +pub struct FilterIter<I, M>(I, M) + where I: Iterator<Item = Result<TimeType>>, + M: Matcher; + +impl<I, M> FilterIter<I, M> + where I: Iterator<Item = Result<TimeType>>, + M: Matcher +{ + fn new(i: I, m: M) -> FilterIter<I, M> { + FilterIter(i, m) + } +} + +impl<I, M> Iterator for FilterIter<I, M> + where I: Iterator<Item = Result<TimeType>>, + M: Matcher +{ + type Item = Result<TimeType>; + + fn next(&mut self) -> Option<Self::Item> { + 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<M: Matcher> : Iterator<Item = Result<TimeType>> + Sized { + fn every(self, M) -> FilterIter<Self, M>; +} + +impl<I, M> EveryFilter<M> for I + where I: Iterator<Item = Result<TimeType>>, + M: Matcher +{ + fn every(self, matcher: M) -> FilterIter<Self, M> { + FilterIter::new(self, matcher) + } +} + pub mod extensions { use timetype::TimeType as TT; use super::Iter; @@ -234,6 +294,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)) @@ -322,3 +383,49 @@ 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); + } +} + +#[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)); + } +} + @@ -2,10 +2,14 @@ extern crate error_chain; extern crate chrono; +#[cfg(feature = "with-filters")] +extern crate filters; + pub mod error; 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..d574090 --- /dev/null +++ b/src/matcher.rs @@ -0,0 +1,48 @@ + +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<bool>; +} + +impl Matcher for Day { + + fn matches(&self, tt: &TimeType) -> Result<bool> { + 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<bool> { + let this : u32 = self.clone().into(); + tt.get_moment() + .map(|mom| this == mom.month()) + .ok_or(KE::from_kind(KEK::ArgumentErrorNotAMoment(tt.name()))) + } + +} + +#[cfg(feature = "with-filters")] +use filters::filter::*; + +#[cfg(feature = "with-filters")] +impl<F> Matcher for F + where F: Filter<TimeType> +{ + fn matches(&self, tt: &TimeType) -> Result<bool> { + Ok(self.filter(tt)) + } +} + |