diff --git a/Cargo.toml b/Cargo.toml index 1e542ec..ea9e511 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ appveyor = { repository = "assert-rs/predicates-rs" } difference = { version = "2.0", optional = true } regex = { version="1.0", optional = true } float-cmp = { version="0.4", optional = true } +treeline = { version = "0.1", optional = true } [features] default = ["difference", "regex", "float-cmp"] diff --git a/examples/tree_eval.rs b/examples/tree_eval.rs new file mode 100644 index 0000000..2a939ff --- /dev/null +++ b/examples/tree_eval.rs @@ -0,0 +1,9 @@ +extern crate predicates; + +fn main() { + use predicates::prelude::*; + let predicate_fn = predicate::ne(5).not().and(predicate::ge(5)); + + let (result, output) = predicate_fn.tree_eval(&5); + println!("{}", output); +} diff --git a/src/boolean.rs b/src/boolean.rs index c87ca84..cca059b 100644 --- a/src/boolean.rs +++ b/src/boolean.rs @@ -11,6 +11,9 @@ use std::marker::PhantomData; use std::fmt; +#[cfg(feature = "treeline")] +use treeline::Tree; + use Predicate; /// Predicate that combines two `Predicate`s, returning the AND of the results. @@ -21,7 +24,7 @@ pub struct AndPredicate where M1: Predicate, M2: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { a: M1, b: M2, @@ -32,7 +35,7 @@ impl AndPredicate where M1: Predicate, M2: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { /// Create a new `AndPredicate` over predicates `a` and `b`. pub fn new(a: M1, b: M2) -> AndPredicate { @@ -48,18 +51,37 @@ impl Predicate for AndPredicate where M1: Predicate, M2: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { fn eval(&self, item: &Item) -> bool { self.a.eval(item) && self.b.eval(item) } + + #[cfg(feature = "treeline")] + fn make_tree(&self, item: &Item) -> Tree { + Tree::new( + format!( + "{} {}", + self.stringify(item), + ::core::pass_fail(self.eval(item)) + ), + vec![ + self.a.make_tree(item), + self.b.make_tree(item), + ] + ) + } + + fn stringify(&self, item: &Item) -> String { + format!("{} && {}", self.a.stringify(item), self.b.stringify(item)) + } } impl fmt::Display for AndPredicate where M1: Predicate, M2: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "({} && {})", self.a, self.b) @@ -74,7 +96,7 @@ pub struct OrPredicate where M1: Predicate, M2: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { a: M1, b: M2, @@ -85,7 +107,7 @@ impl OrPredicate where M1: Predicate, M2: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { /// Create a new `OrPredicate` over predicates `a` and `b`. pub fn new(a: M1, b: M2) -> OrPredicate { @@ -101,7 +123,7 @@ impl Predicate for OrPredicate where M1: Predicate, M2: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { fn eval(&self, item: &Item) -> bool { self.a.eval(item) || self.b.eval(item) @@ -112,7 +134,7 @@ impl fmt::Display for OrPredicate where M1: Predicate, M2: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "({} || {})", self.a, self.b) @@ -126,7 +148,7 @@ where pub struct NotPredicate where M: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { inner: M, _phantom: PhantomData, @@ -135,7 +157,7 @@ where impl NotPredicate where M: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { /// Create a new `NotPredicate` over predicate `inner`. pub fn new(inner: M) -> NotPredicate { @@ -149,17 +171,33 @@ where impl Predicate for NotPredicate where M: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { fn eval(&self, item: &Item) -> bool { !self.inner.eval(item) } + + #[cfg(feature = "treeline")] + fn make_tree(&self, item: &Item) -> Tree { + Tree::new( + format!( + "{} {}", + self.stringify(item), + ::core::pass_fail(self.eval(item)) + ), + vec![self.inner.make_tree(item)] + ) + } + + fn stringify(&self, item: &Item) -> String { + format!("!({})", self.inner.stringify(item)) + } } impl fmt::Display for NotPredicate where M: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "(! {})", self.inner) @@ -167,7 +205,7 @@ where } /// `Predicate` extension that adds boolean logic. -pub trait PredicateBooleanExt +pub trait PredicateBooleanExt where Self: Predicate, { @@ -233,6 +271,6 @@ where impl PredicateBooleanExt for P where P: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { } diff --git a/src/boxed.rs b/src/boxed.rs index 5113b54..5e8a893 100644 --- a/src/boxed.rs +++ b/src/boxed.rs @@ -15,11 +15,11 @@ use Predicate; /// `Predicate` that wraps another `Predicate` as a trait object, allowing /// sized storage of predicate types. -pub struct BoxPredicate(Box + Send + Sync>); +pub struct BoxPredicate(Box + Send + Sync>); impl BoxPredicate where - Item: ?Sized, + Item: ?Sized + fmt::Debug, { /// Creates a new `BoxPredicate`, a wrapper around a dynamically-dispatched /// `Predicate` type with useful trait impls. @@ -33,7 +33,7 @@ where impl fmt::Debug for BoxPredicate where - Item: ?Sized, + Item: ?Sized + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("BoxPredicate").finish() @@ -42,7 +42,7 @@ where impl fmt::Display for BoxPredicate where - Item: ?Sized, + Item: ?Sized + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) @@ -51,7 +51,7 @@ where impl Predicate for BoxPredicate where - Item: ?Sized, + Item: ?Sized + fmt::Debug, { fn eval(&self, variable: &Item) -> bool { self.0.eval(variable) @@ -59,7 +59,7 @@ where } /// `Predicate` extension for boxing a `Predicate`. -pub trait PredicateBoxExt +pub trait PredicateBoxExt where Self: Predicate, { @@ -98,5 +98,6 @@ where impl PredicateBoxExt for P where P: Predicate, + Item: ?Sized + fmt::Debug { } diff --git a/src/constant.rs b/src/constant.rs index d67cb94..8d5a367 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -22,7 +22,7 @@ pub struct BooleanPredicate { _phantom: PhantomData, } -impl Predicate for BooleanPredicate { +impl Predicate for BooleanPredicate { fn eval(&self, _variable: &Item) -> bool { self.retval } diff --git a/src/core.rs b/src/core.rs index dd48e0b..eb64db3 100644 --- a/src/core.rs +++ b/src/core.rs @@ -8,6 +8,17 @@ use std::fmt; +#[cfg(feature = "treeline")] +use treeline::Tree; + +pub(crate) fn pass_fail(b: bool) -> &'static str { + if b { + "PASSED" + } else { + "FAILED" + } +} + /// Trait for generically evaluating a type against a dynamically created /// predicate function. /// @@ -15,8 +26,25 @@ use std::fmt; /// mean that the evaluated item is in some sort of pre-defined set. This is /// different from `Ord` and `Eq` in that an `item` will almost never be the /// same type as the implementing `Predicate` type. -pub trait Predicate: fmt::Display { +pub trait Predicate: fmt::Display { /// Execute this `Predicate` against `variable`, returning the resulting /// boolean. fn eval(&self, variable: &Item) -> bool; + + /// TODO + fn stringify(&self, _item: &Item) -> String { + unimplemented!() + } + + /// TODO + #[cfg(feature = "treeline")] + fn make_tree(&self, _item: &Item) -> Tree { + unimplemented!() + } + + /// TODO + #[cfg(feature = "treeline")] + fn tree_eval(&self, item: &Item) -> (bool, Tree) { + (self.eval(item), self.make_tree(item)) + } } diff --git a/src/function.rs b/src/function.rs index 6d9184e..cb3db14 100644 --- a/src/function.rs +++ b/src/function.rs @@ -51,11 +51,12 @@ where } } -impl Predicate for FnPredicate +impl Predicate for FnPredicate where - F: Fn(&T) -> bool, + F: Fn(&Item) -> bool, + Item: fmt::Debug { - fn eval(&self, variable: &T) -> bool { + fn eval(&self, variable: &Item) -> bool { (self.function)(variable) } } diff --git a/src/lib.rs b/src/lib.rs index b09467d..a720bd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,6 +97,8 @@ extern crate difference; extern crate float_cmp; #[cfg(feature = "regex")] extern crate regex; +#[cfg(feature = "treeline")] +extern crate treeline; pub mod prelude; diff --git a/src/name.rs b/src/name.rs index f5592da..8ac3f70 100644 --- a/src/name.rs +++ b/src/name.rs @@ -20,7 +20,7 @@ use Predicate; pub struct NamePredicate where M: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { inner: M, name: &'static str, @@ -30,7 +30,7 @@ where impl Predicate for NamePredicate where M: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { fn eval(&self, item: &Item) -> bool { self.inner.eval(item) @@ -40,7 +40,7 @@ where impl fmt::Display for NamePredicate where M: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name) @@ -48,7 +48,7 @@ where } /// `Predicate` extension that adds naming predicate expressions. -pub trait PredicateNameExt +pub trait PredicateNameExt where Self: Predicate, { @@ -77,6 +77,6 @@ where impl PredicateNameExt for P where P: Predicate, - Item: ?Sized, + Item: ?Sized + fmt::Debug, { } diff --git a/src/ord.rs b/src/ord.rs index baee5f1..f706ab1 100644 --- a/src/ord.rs +++ b/src/ord.rs @@ -10,6 +10,9 @@ use std::fmt; +#[cfg(feature = "treeline")] +use treeline::Tree; + use Predicate; #[derive(Clone, Copy, Debug)] @@ -41,16 +44,31 @@ where op: EqOps, } -impl Predicate for EqPredicate +impl Predicate for EqPredicate where - T: PartialEq + fmt::Debug, + Item: PartialEq + fmt::Debug, { - fn eval(&self, variable: &T) -> bool { + fn eval(&self, variable: &Item) -> bool { match self.op { EqOps::Equal => variable.eq(&self.constant), EqOps::NotEqual => variable.ne(&self.constant), } } + + #[cfg(feature = "treeline")] + fn make_tree(&self, item: &Item) -> Tree { + Tree::root( + format!( + "{} {}", + self.stringify(item), + ::core::pass_fail(self.eval(item)) + ) + ) + } + + fn stringify(&self, item: &Item) -> String { + format!("{:?} {} {:?}", item, self.op, self.constant) + } } impl fmt::Display for EqPredicate @@ -139,11 +157,11 @@ where op: OrdOps, } -impl Predicate for OrdPredicate +impl Predicate for OrdPredicate where - T: PartialOrd + fmt::Debug, + Item: PartialOrd + fmt::Debug, { - fn eval(&self, variable: &T) -> bool { + fn eval(&self, variable: &Item) -> bool { match self.op { OrdOps::LessThan => variable.lt(&self.constant), OrdOps::LessThanOrEqual => variable.le(&self.constant), @@ -151,6 +169,21 @@ where OrdOps::GreaterThan => variable.gt(&self.constant), } } + + #[cfg(feature = "treeline")] + fn make_tree(&self, item: &Item) -> Tree { + Tree::root( + format!( + "{} {}", + self.stringify(item), + ::core::pass_fail(self.eval(item)) + ) + ) + } + + fn stringify(&self, item: &Item) -> String { + format!("{:?} {} {:?}", item, self.op, self.constant) + } } impl fmt::Display for OrdPredicate