diff options
author | Matthias Beyer <mail@beyermatthias.de> | 2017-10-06 17:25:41 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-10-06 17:25:41 +0200 |
commit | 94dd4b0f85e74f56356f6fe075d7f0db845b382b (patch) | |
tree | 9fe8b2cef73d991886440d40a885550f2f074ce7 | |
parent | 3d99f7879186c6be4c4b1ef86d02113bf6685e8b (diff) | |
parent | 453f17fcdd728c7024f00d4fa618596451a6c283 (diff) |
Merge pull request #20 from matthiasbeyer/failable-filter
Failable filter
-rw-r--r-- | src/failable/filter.rs | 419 | ||||
-rw-r--r-- | src/failable/mod.rs | 9 | ||||
-rw-r--r-- | src/failable/ops/and.rs | 35 | ||||
-rw-r--r-- | src/failable/ops/bool.rs | 40 | ||||
-rw-r--r-- | src/failable/ops/map.rs | 54 | ||||
-rw-r--r-- | src/failable/ops/mod.rs | 12 | ||||
-rw-r--r-- | src/failable/ops/not.rs | 33 | ||||
-rw-r--r-- | src/failable/ops/or.rs | 35 | ||||
-rw-r--r-- | src/failable/ops/xor.rs | 36 | ||||
-rw-r--r-- | src/filter.rs | 67 | ||||
-rw-r--r-- | src/impl_traits.rs | 37 | ||||
-rw-r--r-- | src/lib.rs | 3 | ||||
-rw-r--r-- | src/ops/and.rs | 2 | ||||
-rw-r--r-- | src/ops/bool.rs | 2 | ||||
-rw-r--r-- | src/ops/failable.rs | 51 | ||||
-rw-r--r-- | src/ops/map.rs | 35 | ||||
-rw-r--r-- | src/ops/mod.rs | 2 | ||||
-rw-r--r-- | src/ops/not.rs | 2 | ||||
-rw-r--r-- | src/ops/or.rs | 2 | ||||
-rw-r--r-- | src/ops/xor.rs | 2 |
20 files changed, 871 insertions, 7 deletions
diff --git a/src/failable/filter.rs b/src/failable/filter.rs new file mode 100644 index 0000000..050084c --- /dev/null +++ b/src/failable/filter.rs @@ -0,0 +1,419 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +use std::borrow::Borrow; + +pub use failable::ops::and::FailableAnd; +pub use failable::ops::bool::FailableBool; +pub use failable::ops::not::FailableNot; +pub use failable::ops::xor::FailableXOr; +pub use failable::ops::or::FailableOr; +pub use failable::ops::map::{FailableMapInput, FailableMapErr}; + +/// Trait for converting something into a Filter +pub trait IntoFailableFilter<N, E: Sized> { + type IntoFilt: FailableFilter<N, E>; + + fn into_failable_filter(self) -> Self::IntoFilt; +} + +/// All Filters can be turned into Filters +impl<N, E: Sized, I: FailableFilter<N, E>> IntoFailableFilter<N, E> for I { + type IntoFilt = I; + + fn into_failable_filter(self) -> Self::IntoFilt { + self + } +} + +pub trait FailableFilter<N, E> { + /// The function which is used to filter something + fn filter(&self, &N) -> Result<bool, E>; + + /// Helper to invert a filter. + /// + /// ``` + /// # #[derive(Debug)] + /// # struct ErrorStub { } + /// # + /// use filters::failable::filter::FailableFilter; + /// + /// let f = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a == 1) }).not(); + /// + /// assert!(f.filter(&2).unwrap()); + /// ``` + fn not(self) -> FailableNot<Self> + where Self: Sized + { + FailableNot::new(self) + } + + /// Helper to connect two filters via logical OR + /// + /// ``` + /// # #[derive(Debug)] + /// # struct ErrorStub { } + /// # + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a == 1) }); + /// let b = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a == 2) }); + /// let c = a.or(b); + /// + /// assert!(c.filter(&1).unwrap()); + /// assert!(c.filter(&2).unwrap()); + /// assert!(!c.filter(&7).unwrap()); + /// ``` + fn or<F>(self, other: F) -> FailableOr<Self, F::IntoFilt> + where Self: Sized, + F: IntoFailableFilter<N, E> + Sized + { + FailableOr::new(self, other.into_failable_filter()) + } + + /// Helper to connect two filters via logical OR and NOT + /// + /// ``` + /// # #[derive(Debug)] + /// # struct ErrorStub { } + /// # + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a == 1) }); + /// let b = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a == 2) }); + /// let c = a.or_not(b); + /// + /// assert!(c.filter(&1).unwrap()); + /// assert!(!c.filter(&2).unwrap()); + /// assert!(c.filter(&7).unwrap()); + /// ``` + fn or_not<F>(self, other: F) -> FailableOr<Self, FailableNot<F::IntoFilt>> + where Self: Sized, + F: IntoFailableFilter<N, E> + Sized, + { + self.or(FailableNot::new(other.into_failable_filter())) + } + + /// Helper to connect three filters via logical OR + /// + /// ``` + /// # #[derive(Debug)] + /// # struct ErrorStub { } + /// # + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a == 1) }); + /// let b = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a == 2) }); + /// let c = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a == 3) }); + /// let d = a.or3(b, c); + /// + /// assert!(d.filter(&1).unwrap()); + /// assert!(d.filter(&2).unwrap()); + /// assert!(d.filter(&3).unwrap()); + /// assert!(!d.filter(&4).unwrap()); + /// ``` + fn or3<F, F2>(self, other: F, other2: F2) -> FailableOr<Self, FailableOr<F::IntoFilt, F2::IntoFilt>> + where Self: Sized, + F: IntoFailableFilter<N, E> + Sized, + F2: IntoFailableFilter<N, E> + Sized + { + FailableOr::new(self, FailableOr::new(other.into_failable_filter(), other2.into_failable_filter())) + } + + /// Helper to connect two filters via logical NOR + /// + /// ``` + /// # #[derive(Debug)] + /// # struct ErrorStub { } + /// # + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a == 1) }); + /// let b = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a == 2) }); + /// let c = a.nor(b); /* !(a == 1 || a == 2) */ + /// + /// assert!(!c.filter(&1).unwrap()); + /// assert!(!c.filter(&2).unwrap()); + /// assert!(c.filter(&3).unwrap()); + /// assert!(c.filter(&4).unwrap()); + /// ``` + fn nor<F>(self, other: F) -> FailableNot<FailableOr<Self, F>> + where Self: Sized, + { + FailableNot::new(FailableOr::new(self, other)) + } + + /// Helper to connect two filters via logical XOR + /// + /// ``` + /// # #[derive(Debug)] + /// # struct ErrorStub { } + /// # + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a > 3) }); + /// let b = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a < 7) }); + /// let c = a.xor(b); + /// + /// assert!(c.filter(&1).unwrap()); + /// assert!(c.filter(&3).unwrap()); + /// assert!(!c.filter(&4).unwrap()); + /// assert!(!c.filter(&6).unwrap()); + /// assert!(c.filter(&9).unwrap()); + /// ``` + fn xor<F>(self, other: F) -> FailableXOr<Self, F> + where Self: Sized, + { + FailableXOr::new(self, other) + } + + /// Helper to connect two filters via logical AND + /// + /// ``` + /// # #[derive(Debug)] + /// # struct ErrorStub { } + /// # + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a > 1) }); + /// let b = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a < 7) }); + /// let c = a.and(b); + /// + /// assert!(!c.filter(&1).unwrap()); + /// assert!(c.filter(&3).unwrap()); + /// assert!(c.filter(&4).unwrap()); + /// assert!(c.filter(&6).unwrap()); + /// assert!(!c.filter(&9).unwrap()); + /// ``` + fn and<F>(self, other: F) -> FailableAnd<Self, F::IntoFilt> + where Self: Sized, + F: IntoFailableFilter<N, E> + Sized + { + FailableAnd::new(self, other.into_failable_filter()) + } + + /// Helper to connect three filters via logical AND + /// + /// ``` + /// # #[derive(Debug)] + /// # struct ErrorStub { } + /// # + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a > 1) }); + /// let b = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a < 20) }); + /// let c = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a % 2 == 0) }); + /// let d = a.and3(b, c); + /// + /// assert!(!d.filter(&1).unwrap()); + /// assert!(!d.filter(&3).unwrap()); + /// assert!(d.filter(&8).unwrap()); + /// assert!(d.filter(&10).unwrap()); + /// assert!(d.filter(&14).unwrap()); + /// assert!(!d.filter(&15).unwrap()); + /// assert!(!d.filter(&19).unwrap()); + /// ``` + fn and3<F, F2>(self, other: F, other2: F2) -> FailableAnd<Self, FailableAnd<F::IntoFilt, F2::IntoFilt>> + where Self: Sized, + F: IntoFailableFilter<N, E> + Sized, + F2: IntoFailableFilter<N, E> + Sized + { + FailableAnd::new(self, FailableAnd::new(other.into_failable_filter(), other2.into_failable_filter())) + } + + /// Helper to connect two filters via logical AND and NOT + /// + /// ``` + /// # #[derive(Debug)] + /// # struct ErrorStub { } + /// # + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a > 10) }); + /// let b = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a < 20) }); + /// let c = a.and_not(b); + /// + /// assert!(!c.filter(&1).unwrap()); + /// assert!(!c.filter(&3).unwrap()); + /// assert!(!c.filter(&8).unwrap()); + /// assert!(!c.filter(&11).unwrap()); + /// assert!(c.filter(&24).unwrap()); + /// assert!(c.filter(&25).unwrap()); + /// assert!(c.filter(&29).unwrap()); + /// ``` + fn and_not<F>(self, other: F) -> FailableAnd<Self, FailableNot<F::IntoFilt>> + where Self: Sized, + F: IntoFailableFilter<N, E> + Sized + { + self.and(FailableNot::new(other.into_failable_filter())) + } + + /// Helper to connect two filters via logical NAND + /// + /// ``` + /// # #[derive(Debug)] + /// # struct ErrorStub { } + /// # + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a > 10) }); + /// let b = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a < 20) }); + /// let c = a.nand(b); + /// + /// assert!(c.filter(&1).unwrap()); + /// assert!(c.filter(&3).unwrap()); + /// assert!(c.filter(&8).unwrap()); + /// assert!(!c.filter(&11).unwrap()); + /// assert!(!c.filter(&14).unwrap()); + /// assert!(c.filter(&25).unwrap()); + /// assert!(c.filter(&29).unwrap()); + /// ``` + fn nand<F>(self, other: F) -> FailableNot<FailableAnd<Self, F>> + where Self: Sized, + { + FailableNot::new(FailableAnd::new(self, other)) + } + + /// Helper to transform the input of a filter + /// + /// ``` + /// # #[derive(Debug)] + /// # struct ErrorStub { } + /// # + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a > 1) }); + /// let b = (|&a: &i64| -> Result<bool, ErrorStub> { Ok(a < 7) }); + /// let b = b.map_input(|&x: &usize| x as i64); + /// let c = a.and(b); + /// + /// assert!(!c.filter(&1).unwrap()); + /// assert!(c.filter(&3).unwrap()); + /// assert!(c.filter(&4).unwrap()); + /// assert!(c.filter(&6).unwrap()); + /// assert!(!c.filter(&9).unwrap()); + /// ``` + fn map_input<O, B, T, M>(self, map: M) -> FailableMapInput<Self, M, O, B> + where Self: Sized, + M: Fn(&T) -> N, + B: Borrow<O> + Sized + { + FailableMapInput::new(self, map) + } + + /// Helper to transform the input of a filter + /// + /// ``` + /// # use std::fmt; + /// # #[derive(Debug)] + /// # struct ErrorStub { } + /// # + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| -> Result<bool, ErrorStub> { Ok(a > 1) }); + /// let b = (|&a: &usize| -> Result<bool, fmt::Error> { Ok(a < 7) }); + /// let b = b.map_err(|_: fmt::Error| ErrorStub {}); + /// let c = a.and(b); + /// + /// assert!(!c.filter(&1).unwrap()); + /// assert!(c.filter(&3).unwrap()); + /// assert!(c.filter(&4).unwrap()); + /// assert!(c.filter(&6).unwrap()); + /// assert!(!c.filter(&9).unwrap()); + /// ``` + fn map_err<M, OE>(self, map: M) -> FailableMapErr<Self, M, E, OE> + where Self: Sized, + M: Fn(E) -> OE + { + FailableMapErr::new(self, map) + } + +} + +/// All closures that take a ref to something and return Result<bool, E> are failable filters +impl<I, E, T: Fn(&I) -> Result<bool, E>> FailableFilter<I, E> for T { + fn filter(&self, other: &I) -> Result<bool, E>{ + self(other) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Debug)] + struct StupError { } + + #[test] + fn compile_test() { + let a = |r: &i32| -> Result<bool, StupError> { Ok(true) }; + + assert!(a.filter(&1).unwrap()); + } + + #[test] + fn test_error_return() { + let a = |_: &i32| -> Result<bool, StupError> { Err(StupError {}) }; + + assert!(a.filter(&1).is_err()); + } + + #[test] + fn test_error_return_and_chained() { + let a = |_: &i32| -> Result<bool, StupError> { Err(StupError {}) }; + let b = |_: &i32| -> Result<bool, StupError> { Ok(true) }; + let c = |_: &i32| -> Result<bool, StupError> { Ok(true) }; + let d = |_: &i32| -> Result<bool, StupError> { Ok(true) }; + + let e = d.and(b).and(c).and(a); + + assert!(e.filter(&1).is_err()); + } + + #[test] + fn test_error_return_or_chained() { + let a = |_: &i32| -> Result<bool, StupError> { Err(StupError {}) }; + let b = |_: &i32| -> Result<bool, StupError> { Ok(true) }; + let c = |_: &i32| -> Result<bool, StupError> { Ok(true) }; + let d = |_: &i32| -> Result<bool, StupError> { Ok(true) }; + + let e = a.or(b).or(c).or(d); + + assert!(e.filter(&1).is_err()); + } + + #[test] + fn test_both_filter_types() { + use filter::Filter; + + let a = |_: &i32| -> Result<bool, StupError> { Ok(true) }; + let b = |_: &i32| -> bool { true }; + let c = |_: &i32| -> Result<bool, StupError> { Ok(true) }; + let d = |_: &i32| -> bool { false }; + + let e = a // true + .and(b.into_failable().map_err(|_| StupError {})) // true + .xor(c) // true + .or(d.into_failable().map_err(|_| StupError {})); // true + + assert!(!e.filter(&1).unwrap()); + } + + + #[test] + fn test_both_filter_types_in_one_scope() { + use filter::Filter; + use failable::filter::FailableFilter; + + let failable = |_: &i32| -> Result<bool, StupError> { Ok(true) }; + let unfailable = |_: &i32| -> bool { true }; + + assert!(failable.filter(&1).unwrap()); + assert!(unfailable.filter(&1)); + + } +} + diff --git a/src/failable/mod.rs b/src/failable/mod.rs new file mode 100644 index 0000000..2ae44a6 --- /dev/null +++ b/src/failable/mod.rs @@ -0,0 +1,9 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +pub mod filter; +pub mod ops; + diff --git a/src/failable/ops/and.rs b/src/failable/ops/and.rs new file mode 100644 index 0000000..1d195a6 --- /dev/null +++ b/src/failable/ops/and.rs @@ -0,0 +1,35 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +//! AND implementation. +//! +//! Will be automatically included when including `filter::Filter`, so importing this module +//! shouldn't be necessary. +//! + +use failable::filter::FailableFilter; + +#[must_use = "filters are lazy and do nothing unless consumed"] +#[derive(Clone)] +pub struct FailableAnd<T, U>(T, U); + +impl<T, U> FailableAnd<T, U> { + + pub fn new(a: T, b: U) -> FailableAnd<T, U> { + FailableAnd(a, b) + } + +} + +impl<N, E, T, U> FailableFilter<N, E> for FailableAnd<T, U> + where T: FailableFilter<N, E>, + U: FailableFilter<N, E> +{ + fn filter(&self, e: &N) -> Result<bool, E> { + Ok(try!(self.0.filter(e)) && try!(self.1.filter(e))) + } +} + diff --git a/src/failable/ops/bool.rs b/src/failable/ops/bool.rs new file mode 100644 index 0000000..82c0ac9 --- /dev/null +++ b/src/failable/ops/bool.rs @@ -0,0 +1,40 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +//! FailableBool Filter implementation, so we can insert this in filter construction +//! +//! Will be automatically included when including `filter::Filter`, so importing this module +//! shouldn't be necessary. +//! + +use failable::filter::FailableFilter; + +#[must_use = "filters are lazy and do nothing unless consumed"] +#[derive(Clone)] +pub struct FailableBool(bool); + +impl FailableBool { + + pub fn new(b: bool) -> FailableBool { + FailableBool(b) + } + +} + +impl From<bool> for FailableBool { + + fn from(b: bool) -> FailableBool { + FailableBool::new(b) + } + +} + +impl<N, E> FailableFilter<N, E> for FailableBool { + fn filter(&self, _: &N) -> Result<bool, E> { + Ok(self.0) + } +} + diff --git a/src/failable/ops/map.rs b/src/failable/ops/map.rs new file mode 100644 index 0000000..ccec3d3 --- /dev/null +++ b/src/failable/ops/map.rs @@ -0,0 +1,54 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +//! Map implementation. +//! +//! Will be automatically included when including `filter::Filter`, so importing this module +//! shouldn't be necessary. +//! +use std::marker::PhantomData; +use std::borrow::Borrow; + +use failable::filter::FailableFilter; + +#[must_use = "filters are lazy and do nothing unless consumed"] +#[derive(Clone)] +pub struct FailableMapInput<F, M, FT, B>(F, M, PhantomData<FT>, PhantomData<B>); + +impl<F, M, FT, B> FailableMapInput<F, M, FT, B> { + pub fn new(a: F, m: M) -> FailableMapInput<F, M, FT, B> { + FailableMapInput(a, m, PhantomData, PhantomData) + } +} + +impl<FT, E, F, T, B, M> FailableFilter<T, E> for FailableMapInput<F, M, FT, B> + where F: FailableFilter<FT, E>, + B: Borrow<FT> + Sized, + M: Fn(&T) -> B +{ + fn filter(&self, e: &T) -> Result<bool, E> { + self.0.filter(self.1(e).borrow()) + } +} + +#[must_use = "filters are lazy and do nothing unless consumed"] +#[derive(Clone)] +pub struct FailableMapErr<F, M, FE, E>(F, M, PhantomData<FE>, PhantomData<E>); + +impl<F, M, FE, E> FailableMapErr<F, M, FE, E> { + pub fn new(a: F, m: M) -> FailableMapErr<F, M, FE, E> { + FailableMapErr(a, m, PhantomData, PhantomData) + } +} + +impl<FE, E, F, T, M> FailableFilter<T, E> for FailableMapErr<F, M, FE, E> + where F: FailableFilter<T, FE>, + M: Fn(FE) -> E +{ + fn filter(&self, e: &T) -> Result<bool, E> { + self.0.filter(e).map_err(&self.1) + } +} diff --git a/src/failable/ops/mod.rs b/src/failable/ops/mod.rs new file mode 100644 index 0000000..618c0e8 --- /dev/null +++ b/src/failable/ops/mod.rs @@ -0,0 +1,12 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +pub mod and; +pub mod bool; +pub mod not; +pub mod xor; +pub mod or; +pub mod map; diff --git a/src/failable/ops/not.rs b/src/failable/ops/not.rs new file mode 100644 index 0000000..25c3e3b --- /dev/null +++ b/src/failable/ops/not.rs @@ -0,0 +1,33 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +//! NOT implementation. +//! +//! Will be automatically included when including `filter::Filter`, so importing this module +//! shouldn't be necessary. +//! + +use failable::filter::FailableFilter; + +#[must_use = "filters are lazy and do nothing unless consumed"] +#[derive(Clone)] +pub struct FailableNot<T>(T); + +impl<T> FailableNot<T> { + + pub fn new(a: T) -> FailableNot<T> { + FailableNot(a) + } + +} + +impl<N, E, T> FailableFilter<N, E> for FailableNot<T> + where T: FailableFilter<N, E> +{ + fn filter(&self, e: &N) -> Result<bool, E> { + self.0.filter(e).map(|b| !b) + } +} diff --git a/src/failable/ops/or.rs b/src/failable/ops/or.rs new file mode 100644 index 0000000..45289c3 --- /dev/null +++ b/src/failable/ops/or.rs @@ -0,0 +1,35 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +//! OR implementation. +//! +//! Will be automatically included when including `filter::Filter`, so importing this module +//! shouldn't be necessary. +//! + +use failable::filter::FailableFilter; + +#[must_use = "filters are lazy and do nothing unless consumed"] +#[derive(Clone)] +pub struct FailableOr<T, U>(T, U); + +impl<T, U> FailableOr<T, U> { + + pub fn new(a: T, b: U) -> FailableOr<T, U> { + FailableOr(a, b) + } + +} + +impl<N, E, T, U> FailableFilter<N, E> for FailableOr<T, U> + where T: FailableFilter<N, E>, + U: FailableFilter<N, E> +{ + fn filter(&self, e: &N) -> Result<bool, E> { + Ok(try!(self.0.filter(e)) || try!(self.1.filter(e))) + } +} + diff --git a/src/failable/ops/xor.rs b/src/failable/ops/xor.rs new file mode 100644 index 0000000..ecc38ce --- /dev/null +++ b/src/failable/ops/xor.rs @@ -0,0 +1,36 @@ +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// + +//! XOR implementation. +//! +//! Will be automatically included when including `filter::Filter`, so importing this module +//! shouldn't be necessary. +//! + +use failable::filter::FailableFilter; + +#[must_use = "filters are lazy and do nothing unless consumed"] +#[derive(Clone)] +pub struct FailableXOr<T, U>(T, U); + +impl<T, U> FailableXOr<T, U> { + + pub fn new(a: T, b: U) -> FailableXOr<T, U> { + FailableXOr(a, b) + } + +} + +impl<N, E, T, U> FailableFilter<N, E> for FailableXOr<T, U> + where T: FailableFilter<N, E>, + U: FailableFilter<N, E> +{ + fn filter(&self, e: &N) -> Result<bool, E> { + Ok(try!(self.0.filter(e)) ^ try!(self.1.filter(e))) + } +} + + diff --git a/src/filter.rs b/src/filter.rs index ac0d7b1..9d92c6f 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -6,12 +6,15 @@ //! The filter implementation //! +use std::borrow::Borrow; pub use ops::and::And; pub use ops::bool::Bool; pub use ops::not::Not; pub use ops::xor::XOr; pub use ops::or::Or; +pub use ops::map::MapInput; +pub use ops::failable::{IntoFailable, AsFailable}; /// Trait for converting something into a Filter pub trait IntoFilter<N> { @@ -36,7 +39,7 @@ impl<I, T: Fn(&I) -> bool> Filter<I> for T { } } -/// The filter trai +/// The filter trait pub trait Filter<N> { /// The function which is used to filter something @@ -256,6 +259,68 @@ pub trait Filter<N> { Not::new(And::new(self, other)) } + /// Helper to transform the input of a filter + /// + /// ``` + /// use filters::filter::Filter; + /// + /// let a = (|&a: &usize| { a > 1 }); + /// let b = (|&a: &i64| { a < 7 }); + /// let b = b.map_input(|&x: &usize| { x as i64 }); + /// let c = a.and(b); + /// + /// assert!(!c.filter(&1)); + /// assert!(c.filter(&3)); + /// assert!(c.filter(&4)); + /// assert!(c.filter(&6)); + /// assert!(!c.filter(&9)); + /// ``` + fn map_input<O, B, T, M>(self, map: M) -> MapInput<Self, M, O, B> + where Self: Sized, + M: Fn(&T) -> N, + B: Borrow<O> + Sized + { + MapInput::new(self, map) + } + + /// Helper to transform a filter into a FailableFilter + /// + /// ``` + /// use filters::filter::Filter; + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| { a > 5 }); + /// let a = a.into_failable(); + /// + /// assert_eq!(a.filter(&3), Ok(false)); + /// assert_eq!(a.filter(&5), Ok(false)); + /// assert_eq!(a.filter(&7), Ok(true)); + /// assert_eq!(a.filter(&9), Ok(true)); + /// ``` + fn into_failable(self) -> IntoFailable<Self, ()> + where Self: Sized + { + IntoFailable::new(self) + } + + /// Helper to borrow a filter as a FailbleFilter + /// + /// ``` + /// use filters::filter::Filter; + /// use filters::failable::filter::FailableFilter; + /// + /// let a = (|&a: &usize| { a > 5 }); + /// let b = a.as_failable(); + /// + /// assert_eq!(a.filter(&3), false); + /// assert_eq!(b.filter(&3), Ok(false)); + /// assert_eq!(a.filter(&7), true); + /// assert_eq!(b.filter(&7), Ok(true)); + fn as_failable<'a>(&'a self) -> AsFailable<'a, Self, ()> + where Self: 'a + { + AsFailable::new(self) + } } diff --git a/src/impl_traits.rs b/src/impl_traits.rs index a5a8c57..03e616e 100644 --- a/src/impl_traits.rs +++ b/src/impl_traits.rs @@ -67,3 +67,40 @@ macro_rules! impl_operators { impl_operators!($struct_ident, $self_var $arg_var $filter_impl, ); }; } + +/// Variant of impl_operators!() macro for FailableFilter types +#[macro_export] +macro_rules! impl_failable_operators { + ($struct_ident:ident, $self_var: ident $arg_var: ident $filter_impl:block, $( $generic:ident ),*) => { + #[cfg(feature = "unstable-filter-as-fn")] + impl<'a, I, $( $generic: Filter<I>, )*> FnOnce<(&'a I,)> for $struct_ident<$( $generic, )*> { + type Output = Result<bool, Error>; + extern "rust-call" fn call_once<'b>(self, (arg,): (&'a I,)) -> Self::Output { + self.filter(arg) + } + } + + #[cfg(feature = "unstable-filter-as-fn")] + impl<'a, I, $( $generic: Filter<I>, )*> FnMut<(&'a I,)> for $struct_ident<$( $generic, )*> { + extern "rust-call" fn call_mut<'b>(&mut self, (arg,): (&'a I,)) -> Self::Output { + self.filter(arg) + } + } + + #[cfg(feature = "unstable-filter-as-fn")] + impl<'a, I, $( $generic: Filter<I>, )*> Fn<(&'a I,)> for $struct_ident<$( $generic, )*> { + #[allow(unused_variables)] + extern "rust-call" fn call<'b>(&$self_var, ($arg_var,): (&'a I,)) -> Self::Output $filter_impl + } + + #[cfg(not(feature = "unstable-filter-as-fn"))] + impl<I, $( $generic: FailableFilter<I>, )*> FailableFilter<I> for $struct_ident<$( $generic, )*> { + #[allow(unused_variables)] + fn filter(&$self_var, $arg_var: &I) -> Result<bool, Error> $filter_impl + } + }; + ($struct_ident:ident, $self_var: ident $arg_var: ident $filter_impl: block) => { + impl_operators!($struct_ident, $self_var $arg_var $filter_impl, ); + }; +} + @@ -74,8 +74,9 @@ #![doc(html_root_url = "https://matthiasbeyer.github.io/filters/")] #![cfg_attr(feature = "unstable-filter-as-fn", feature(unboxed_closures, fn_traits))] -pub mod filter; #[macro_use] pub mod impl_traits; +pub mod filter; +pub mod failable; pub mod ops; diff --git a/src/ops/and.rs b/src/ops/and.rs index 281b906..eb9f347 100644 --- a/src/ops/and.rs +++ b/src/ops/and.rs @@ -6,7 +6,7 @@ //! AND implementation. //! -//! Will be automatically included when incluing `filter::Filter`, so importing this module +//! Will be automatically included when including `filter::Filter`, so importing this module //! shouldn't be necessary. //! u |