summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2017-09-26 17:58:38 +0200
committerMatthias Beyer <mail@beyermatthias.de>2017-09-26 17:58:38 +0200
commit874fcc34d50d6d69e30cc3ce6ef14e617354fe4c (patch)
tree900ca070850269155815499b88307566cecd0a50
parent79923c0cd6ed917b3b0463a66d0a44df85165e23 (diff)
parent4be0e3b361615a84ecf1402b827a81ca62e95bc5 (diff)
Merge branch 'more-iter'
-rw-r--r--Cargo.toml7
-rw-r--r--src/error.rs5
-rw-r--r--src/indicator.rs42
-rw-r--r--src/iter.rs119
-rw-r--r--src/lib.rs4
-rw-r--r--src/matcher.rs48
6 files changed, 219 insertions, 6 deletions
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/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));
+ }
+}
+
diff --git a/src/lib.rs b/src/lib.rs
index 60bcfc1..b537b14 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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))
+ }
+}
+