summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2017-10-06 17:25:41 +0200
committerGitHub <noreply@github.com>2017-10-06 17:25:41 +0200
commit94dd4b0f85e74f56356f6fe075d7f0db845b382b (patch)
tree9fe8b2cef73d991886440d40a885550f2f074ce7
parent3d99f7879186c6be4c4b1ef86d02113bf6685e8b (diff)
parent453f17fcdd728c7024f00d4fa618596451a6c283 (diff)
Merge pull request #20 from matthiasbeyer/failable-filter
Failable filter
-rw-r--r--src/failable/filter.rs419
-rw-r--r--src/failable/mod.rs9
-rw-r--r--src/failable/ops/and.rs35
-rw-r--r--src/failable/ops/bool.rs40
-rw-r--r--src/failable/ops/map.rs54
-rw-r--r--src/failable/ops/mod.rs12
-rw-r--r--src/failable/ops/not.rs33
-rw-r--r--src/failable/ops/or.rs35
-rw-r--r--src/failable/ops/xor.rs36
-rw-r--r--src/filter.rs67
-rw-r--r--src/impl_traits.rs37
-rw-r--r--src/lib.rs3
-rw-r--r--src/ops/and.rs2
-rw-r--r--src/ops/bool.rs2
-rw-r--r--src/ops/failable.rs51
-rw-r--r--src/ops/map.rs35
-rw-r--r--src/ops/mod.rs2
-rw-r--r--src/ops/not.rs2
-rw-r--r--src/ops/or.rs2
-rw-r--r--src/ops/xor.rs2
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, );
+ };
+}
+
diff --git a/src/lib.rs b/src/lib.rs
index 3c63624..7da2cc5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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