diff --git a/src/boolean.rs b/src/boolean.rs index 8b3c434..c87ca84 100644 --- a/src/boolean.rs +++ b/src/boolean.rs @@ -9,6 +9,7 @@ //! Definition of boolean logic combinators over `Predicate`s. use std::marker::PhantomData; +use std::fmt; use Predicate; @@ -54,6 +55,17 @@ where } } +impl fmt::Display for AndPredicate +where + M1: Predicate, + M2: Predicate, + Item: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({} && {})", self.a, self.b) + } +} + /// Predicate that combines two `Predicate`s, returning the OR of the results. /// /// This is created by the `Predicate::or` function. @@ -96,6 +108,17 @@ where } } +impl fmt::Display for OrPredicate +where + M1: Predicate, + M2: Predicate, + Item: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({} || {})", self.a, self.b) + } +} + /// Predicate that returns a `Predicate` taking the logical NOT of the result. /// /// This is created by the `Predicate::not` function. @@ -133,6 +156,16 @@ where } } +impl fmt::Display for NotPredicate +where + M: Predicate, + Item: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "(! {})", self.inner) + } +} + /// `Predicate` extension that adds boolean logic. pub trait PredicateBooleanExt where diff --git a/src/boxed.rs b/src/boxed.rs index e7952d5..5113b54 100644 --- a/src/boxed.rs +++ b/src/boxed.rs @@ -45,7 +45,7 @@ where Item: ?Sized, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "BoxPredicate") + write!(f, "{}", self.0) } } diff --git a/src/constant.rs b/src/constant.rs index 54e5666..d67cb94 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -9,6 +9,7 @@ //! Definition of a constant (always true or always false) `Predicate`. use std::marker::PhantomData; +use std::fmt; use Predicate; @@ -27,6 +28,12 @@ impl Predicate for BooleanPredicate { } } +impl fmt::Display for BooleanPredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.retval) + } +} + /// Creates a new `Predicate` that always returns `true`. /// /// # Examples diff --git a/src/core.rs b/src/core.rs index 7e0ea3b..dd48e0b 100644 --- a/src/core.rs +++ b/src/core.rs @@ -6,6 +6,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::fmt; + /// Trait for generically evaluating a type against a dynamically created /// predicate function. /// @@ -13,7 +15,7 @@ /// 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 { +pub trait Predicate: fmt::Display { /// Execute this `Predicate` against `variable`, returning the resulting /// boolean. fn eval(&self, variable: &Item) -> bool; diff --git a/src/float/close.rs b/src/float/close.rs index bfc5e76..ef25799 100644 --- a/src/float/close.rs +++ b/src/float/close.rs @@ -6,6 +6,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::fmt; + use float_cmp::ApproxEq; use float_cmp::Ulps; @@ -84,6 +86,16 @@ impl Predicate for IsClosePredicate { } } +impl fmt::Display for IsClosePredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "var ~= {} +/- {} ({})", + self.target, self.epsilon, self.ulps + ) + } +} + /// Create a new `Predicate` that ensures two numbers are "close" enough, understanding that /// rounding errors occur. /// diff --git a/src/function.rs b/src/function.rs index a064f88..6d9184e 100644 --- a/src/function.rs +++ b/src/function.rs @@ -9,6 +9,7 @@ //! Definition of `Predicate` for wrapping a `Fn(&T) -> bool` use std::marker::PhantomData; +use std::fmt; use Predicate; @@ -20,9 +21,36 @@ where F: Fn(&T) -> bool, { function: F, + name: &'static str, _phantom: PhantomData, } +impl FnPredicate +where + F: Fn(&T) -> bool, +{ + /// Provide a descriptive name for this function. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// struct Example { + /// string: String, + /// number: i32, + /// } + /// + /// let string_check = predicate::function(|x: &Example| x.string == "hello") + /// .fn_name("is_hello"); + /// println!("predicate: {}", string_check); + /// ``` + pub fn fn_name(mut self, name: &'static str) -> Self { + self.name = name; + self + } +} + impl Predicate for FnPredicate where F: Fn(&T) -> bool, @@ -32,6 +60,15 @@ where } } +impl fmt::Display for FnPredicate +where + F: Fn(&T) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}(var)", self.name) + } +} + /// Creates a new predicate that wraps over the given function. The returned /// type implements `Predicate` and therefore has all combinators available to /// it. @@ -60,6 +97,7 @@ where { FnPredicate { function: function, + name: "fn", _phantom: PhantomData, } } diff --git a/src/iter.rs b/src/iter.rs index a697407..e0af2ea 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -9,6 +9,7 @@ //! Definition of `Predicate`s for comparisons of membership in a set. use std::collections::HashSet; +use std::fmt; use std::hash::Hash; use std::iter::FromIterator; @@ -26,14 +27,14 @@ use Predicate; #[derive(Debug)] pub struct InPredicate where - T: PartialEq, + T: PartialEq + fmt::Debug, { inner: Vec, } impl InPredicate where - T: Ord, + T: Ord + fmt::Debug, { /// Creates a new predicate that will return `true` when the given `variable` is /// contained with the set of items provided. @@ -64,13 +65,22 @@ where impl Predicate for InPredicate where - T: PartialEq, + T: PartialEq + fmt::Debug, { fn eval(&self, variable: &T) -> bool { self.inner.contains(variable) } } +impl fmt::Display for InPredicate +where + T: PartialEq + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var in {:?}", self.inner) + } +} + /// Creates a new predicate that will return `true` when the given `variable` is /// contained with the set of items provided. /// @@ -99,7 +109,7 @@ where /// ``` pub fn in_iter(iter: I) -> InPredicate where - T: PartialEq, + T: PartialEq + fmt::Debug, I: IntoIterator, { InPredicate { @@ -119,20 +129,29 @@ where #[derive(Debug)] pub struct OrdInPredicate where - T: Ord, + T: Ord + fmt::Debug, { inner: Vec, } impl Predicate for OrdInPredicate where - T: Ord, + T: Ord + fmt::Debug, { fn eval(&self, variable: &T) -> bool { self.inner.binary_search(variable).is_ok() } } +impl fmt::Display for OrdInPredicate +where + T: Ord + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var in {:?}", self.inner) + } +} + /// Predicate that returns `true` if `variable` is a member of the pre-defined /// `HashSet`, otherwise returns `false`. /// @@ -145,20 +164,29 @@ where #[derive(Debug)] pub struct HashableInPredicate where - T: Hash + Eq, + T: Hash + Eq + fmt::Debug, { inner: HashSet, } impl Predicate for HashableInPredicate where - T: Hash + Eq, + T: Hash + Eq + fmt::Debug, { fn eval(&self, variable: &T) -> bool { self.inner.contains(variable) } } +impl fmt::Display for HashableInPredicate +where + T: Hash + Eq + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var in {:?}", self.inner) + } +} + /// Creates a new predicate that will return `true` when the given `variable` is /// contained with the set of items provided. /// @@ -181,7 +209,7 @@ where /// ``` pub fn in_hash(iter: I) -> HashableInPredicate where - T: Hash + Eq, + T: Hash + Eq + fmt::Debug, I: IntoIterator, { HashableInPredicate { diff --git a/src/lib.rs b/src/lib.rs index 8ac621c..b09467d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,6 +34,8 @@ //! ``` //! use predicates::prelude::*; //! +//! use std::fmt; +//! //! // The simplest predicates are `always()` and `never()`, which always returns //! // `true` and always returns `false`, respectively. The values are simply //! // ignored when evaluating against these predicates: @@ -57,7 +59,7 @@ //! assert_eq!(false, between_5_and_10.eval(&11)); //! assert_eq!(false, between_5_and_10.eval(&4)); //! -//! // The `Predicate` trait is pretty simple, requiring only the +//! // The `Predicate` trait is pretty simple, the core of it is an //! // implementation of a `eval` function that takes a single argument and //! // returns a `bool`. Implementing a custom `Predicate` still allows all the //! // usual combinators of the `Predicate` trait to work! @@ -67,6 +69,11 @@ //! *variable == 42 //! } //! } +//! impl fmt::Display for IsTheAnswer { +//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +//! write!(f, "var.is_the_answer()") +//! } +//! } //! //! assert_eq!(true, IsTheAnswer.eval(&42)); //! let almost_the_answer = IsTheAnswer.or(predicate::in_iter(vec![41, 43])); @@ -103,6 +110,7 @@ pub mod constant; pub mod function; pub mod ord; pub mod iter; +pub mod name; // combinators pub mod boolean; diff --git a/src/name.rs b/src/name.rs new file mode 100644 index 0000000..f5592da --- /dev/null +++ b/src/name.rs @@ -0,0 +1,82 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Name predicate expressions. + +use std::fmt; +use std::marker::PhantomData; + +use Predicate; + +/// Augment an existing predicate with a name. +/// +/// This is created by the `PredicateNameExt::name` function. +#[derive(Debug)] +pub struct NamePredicate +where + M: Predicate, + Item: ?Sized, +{ + inner: M, + name: &'static str, + _phantom: PhantomData, +} + +impl Predicate for NamePredicate +where + M: Predicate, + Item: ?Sized, +{ + fn eval(&self, item: &Item) -> bool { + self.inner.eval(item) + } +} + +impl fmt::Display for NamePredicate +where + M: Predicate, + Item: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.name) + } +} + +/// `Predicate` extension that adds naming predicate expressions. +pub trait PredicateNameExt +where + Self: Predicate, +{ + /// Name a predicate expression. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let predicate_fn = predicate::str::is_empty().not().name("non-empty"); + /// println!("{}", predicate_fn); + /// ``` + fn name(self, name: &'static str) -> NamePredicate + where + Self: Sized, + { + NamePredicate { + inner: self, + name, + _phantom: PhantomData, + } + } +} + +impl PredicateNameExt for P +where + P: Predicate, + Item: ?Sized, +{ +} diff --git a/src/ord.rs b/src/ord.rs index 3f2b868..baee5f1 100644 --- a/src/ord.rs +++ b/src/ord.rs @@ -8,6 +8,8 @@ //! Definition of `Predicate`s for comparisons over `Ord` and `Eq` types. +use std::fmt; + use Predicate; #[derive(Clone, Copy, Debug)] @@ -16,19 +18,32 @@ enum EqOps { NotEqual, } +impl fmt::Display for EqOps { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let op = match *self { + EqOps::Equal => "==", + EqOps::NotEqual => "!=", + }; + write!(f, "{}", op) + } +} + /// Predicate that returns `true` if `variable` matches the pre-defined `Eq` /// value, otherwise returns `false`. /// /// This is created by the `predicate::{eq, ne}` functions. #[derive(Debug)] -pub struct EqPredicate { +pub struct EqPredicate +where + T: fmt::Debug, +{ constant: T, op: EqOps, } impl Predicate for EqPredicate where - T: PartialEq, + T: PartialEq + fmt::Debug, { fn eval(&self, variable: &T) -> bool { match self.op { @@ -38,6 +53,15 @@ where } } +impl fmt::Display for EqPredicate +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var {} {:?}", self.op, self.constant) + } +} + /// Creates a new predicate that will return `true` when the given `variable` is /// equal to a pre-defined value. /// @@ -52,7 +76,7 @@ where /// ``` pub fn eq(constant: T) -> EqPredicate where - T: PartialEq, + T: PartialEq + fmt::Debug, { EqPredicate { constant: constant, @@ -74,7 +98,7 @@ where /// ``` pub fn ne(constant: T) -> EqPredicate where - T: PartialEq, + T: PartialEq + fmt::Debug, { EqPredicate { constant: constant, @@ -90,19 +114,34 @@ enum OrdOps { GreaterThan, } +impl fmt::Display for OrdOps { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let op = match *self { + OrdOps::LessThan => "<", + OrdOps::LessThanOrEqual => "<=", + OrdOps::GreaterThanOrEqual => ">=", + OrdOps::GreaterThan => ">", + }; + write!(f, "{}", op) + } +} + /// Predicate that returns `true` if `variable` matches the pre-defined `Ord` /// value, otherwise returns `false`. /// /// This is created by the `predicate::{gt, ge, lt, le}` functions. #[derive(Debug)] -pub struct OrdPredicate { +pub struct OrdPredicate +where + T: fmt::Debug, +{ constant: T, op: OrdOps, } impl Predicate for OrdPredicate where - T: PartialOrd, + T: PartialOrd + fmt::Debug, { fn eval(&self, variable: &T) -> bool { match self.op { @@ -114,6 +153,15 @@ where } } +impl fmt::Display for OrdPredicate +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var {} {:?}", self.op, self.constant) + } +} + /// Creates a new predicate that will return `true` when the given `variable` is /// less than a pre-defined value. /// @@ -128,7 +176,7 @@ where /// ``` pub fn lt(constant: T) -> OrdPredicate where - T: PartialOrd, + T: PartialOrd + fmt::Debug, { OrdPredicate { constant: constant, @@ -151,7 +199,7 @@ where /// ``` pub fn le(constant: T) -> OrdPredicate where - T: PartialOrd, + T: PartialOrd + fmt::Debug, { OrdPredicate { constant: constant, @@ -174,7 +222,7 @@ where /// ``` pub fn ge(constant: T) -> OrdPredicate where - T: PartialOrd, + T: PartialOrd + fmt::Debug, { OrdPredicate { constant: constant, @@ -197,7 +245,7 @@ where /// ``` pub fn gt(constant: T) -> OrdPredicate where - T: PartialOrd, + T: PartialOrd + fmt::Debug, { OrdPredicate { constant: constant, diff --git a/src/path/existence.rs b/src/path/existence.rs index 518e01d..45fa715 100644 --- a/src/path/existence.rs +++ b/src/path/existence.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::fmt; use std::path; use Predicate; @@ -24,6 +25,12 @@ impl Predicate for ExistencePredicate { } } +impl fmt::Display for ExistencePredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}(var)", if self.exists { "exists" } else { "missing" }) + } +} + /// Creates a new `Predicate` that ensures the path exists. /// /// # Examples diff --git a/src/path/ft.rs b/src/path/ft.rs index 7588df2..9122248 100644 --- a/src/path/ft.rs +++ b/src/path/ft.rs @@ -7,6 +7,7 @@ // except according to those terms. use std::path; +use std::fmt; use std::fs; use Predicate; @@ -28,6 +29,17 @@ impl FileType { } } +impl fmt::Display for FileType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let t = match *self { + FileType::File => "file", + FileType::Dir => "dir", + FileType::Symlink => "symlink", + }; + write!(f, "{}", t) + } +} + /// Predicate that checks the `std::fs::FileType`. /// /// This is created by the `predicate::path::is_file`, `predicate::path::is_dir`, and `predicate::path::is_symlink`. @@ -62,6 +74,12 @@ impl Predicate for FileTypePredicate { } } +impl fmt::Display for FileTypePredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var is {}", self.ft) + } +} + /// Creates a new `Predicate` that ensures the path points to a file. /// /// # Examples diff --git a/src/prelude.rs b/src/prelude.rs index 4dc4916..1052804 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -12,6 +12,7 @@ pub use core::Predicate; pub use boolean::PredicateBooleanExt; pub use boxed::PredicateBoxExt; pub use str::PredicateStrExt; +pub use name::PredicateNameExt; /// Predicate factories pub mod predicate { diff --git a/src/str/adapters.rs b/src/str/adapters.rs index 4edd60f..bb97a4a 100644 --- a/src/str/adapters.rs +++ b/src/str/adapters.rs @@ -1,4 +1,5 @@ use std::ffi; +use std::fmt; use std::str; use Predicate; @@ -23,6 +24,15 @@ where } } +impl

fmt::Display for TrimPedicate

+where + P: Predicate, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.p) + } +} + /// Predicate adaper that converts a `str` predicate to byte predicate. /// /// This is created by `pred.from_utf8()`. @@ -54,6 +64,15 @@ where } } +impl

fmt::Display for Utf8Pedicate

+where + P: Predicate, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.p) + } +} + /// `Predicate` extension adapting a `str` Predicate. pub trait PredicateStrExt where diff --git a/src/str/basics.rs b/src/str/basics.rs index 3d477cc..63848db 100644 --- a/src/str/basics.rs +++ b/src/str/basics.rs @@ -6,6 +6,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::fmt; + use Predicate; /// Predicate that checks for empty strings. @@ -20,6 +22,12 @@ impl Predicate for IsEmptyPredicate { } } +impl fmt::Display for IsEmptyPredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var.is_empty()") + } +} + /// Creates a new `Predicate` that ensures a str is empty /// /// # Examples @@ -49,6 +57,12 @@ impl Predicate for StartsWithPredicate { } } +impl fmt::Display for StartsWithPredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var.starts_with({:?})", self.pattern) + } +} + /// Creates a new `Predicate` that ensures a str starts with `pattern` /// /// # Examples @@ -83,6 +97,12 @@ impl Predicate for EndsWithPredicate { } } +impl fmt::Display for EndsWithPredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var.ends_with({:?})", self.pattern) + } +} + /// Creates a new `Predicate` that ensures a str ends with `pattern` /// /// # Examples @@ -137,6 +157,12 @@ impl Predicate for ContainsPredicate { } } +impl fmt::Display for ContainsPredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var.contains({:?})", self.pattern) + } +} + /// Predicate that checks for repeated patterns. /// /// This is created by `predicates::str::contains(...).count`. @@ -152,6 +178,12 @@ impl Predicate for MatchesPredicate { } } +impl fmt::Display for MatchesPredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var.contains({:?}, {})", self.pattern, self.count) + } +} + /// Creates a new `Predicate` that ensures a str contains `pattern` /// /// # Examples diff --git a/src/str/difference.rs b/src/str/difference.rs index 178ed21..ce7eb56 100644 --- a/src/str/difference.rs +++ b/src/str/difference.rs @@ -7,6 +7,7 @@ // except according to those terms. use std::borrow; +use std::fmt; use difference; @@ -91,6 +92,15 @@ impl Predicate for DifferencePredicate { } } +impl fmt::Display for DifferencePredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.op { + DistanceOp::Similar => write!(f, "var - {:?} <= {}", self.orig, self.distance), + DistanceOp::Different => write!(f, "{} < var - {:?}", self.distance, self.orig), + } + } +} + /// Creates a new `Predicate` that diffs two strings. /// /// # Examples diff --git a/src/str/regex.rs b/src/str/regex.rs index d850d04..d7be7a8 100644 --- a/src/str/regex.rs +++ b/src/str/regex.rs @@ -6,6 +6,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::fmt; + use regex; use Predicate; @@ -44,6 +46,12 @@ impl Predicate for RegexPredicate { } } +impl fmt::Display for RegexPredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var.is_match({})", self.re) + } +} + /// Predicate that checks for repeated patterns. /// /// This is created by `predicates::str::is_match(...).count`. @@ -58,6 +66,13 @@ impl Predicate for RegexMatchesPredicate { self.re.find_iter(variable).count() == self.count } } + +impl fmt::Display for RegexMatchesPredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "var.is_match({}).count({})", self.re, self.count) + } +} + /// Creates a new `Predicate` that uses a regular expression to match the string. /// /// # Examples