Skip to content

Commit c589b0c

Browse files
committed
feat: Allow predicates to own object and evaluate against borrowed types
It is very useful to be able to dynamically construct an object and have that object owned by the predicate, yet evaluate against an unowned type related to the owned one. An obvious example is a String being owned by the predicate but being compared against &strs. Therefore, implement Predicate for Eq/OrdPredicate that store an object that implements Borrow for the predicate type, replacing existing impls of Predicate<T> for Eq/OrdPredicate<T> and Eq/OrdPredicate<&T>. This is backwards compatible as there are blanket implementations of Borrow<T> for T and Borrow<T> for &T. Note that Borrow imposes more requirements than are actually required and AsRef would be sufficient. However, AsRef doesn't have a blanket implementation for T and thus the existing impl of Predicate<T> for EqPredicate<T> is still required, but results in a conflict since T may also implement AsRef<T>. Requiring Borrow instead of AsRef is sufficient for common use cases though. This addresses assert-rs#20 more completely.
1 parent ee57a38 commit c589b0c

File tree

1 file changed

+31
-71
lines changed

1 file changed

+31
-71
lines changed

src/ord.rs

Lines changed: 31 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2018 The predicates-rs Project Developers.
1+
// Copyright (c) 2018, 2022 The predicates-rs Project Developers.
22
//
33
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
44
// http://www.apache.org/license/LICENSE-2.0> or the MIT license
@@ -35,26 +35,24 @@ impl fmt::Display for EqOps {
3535
///
3636
/// This is created by the `predicate::{eq, ne}` functions.
3737
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38-
pub struct EqPredicate<T>
39-
where
40-
T: fmt::Debug + PartialEq,
41-
{
38+
pub struct EqPredicate<T> {
4239
constant: T,
4340
op: EqOps,
4441
}
4542

46-
impl<T> Predicate<T> for EqPredicate<T>
43+
impl<P, T> Predicate<P> for EqPredicate<T>
4744
where
48-
T: fmt::Debug + PartialEq,
45+
T: std::borrow::Borrow<P> + fmt::Debug,
46+
P: fmt::Debug + PartialEq + ?Sized,
4947
{
50-
fn eval(&self, variable: &T) -> bool {
48+
fn eval(&self, variable: &P) -> bool {
5149
match self.op {
52-
EqOps::Equal => variable.eq(&self.constant),
53-
EqOps::NotEqual => variable.ne(&self.constant),
50+
EqOps::Equal => variable.eq(self.constant.borrow()),
51+
EqOps::NotEqual => variable.ne(self.constant.borrow()),
5452
}
5553
}
5654

57-
fn find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>> {
55+
fn find_case<'a>(&'a self, expected: bool, variable: &P) -> Option<reflection::Case<'a>> {
5856
utils::default_find_case(self, expected, variable).map(|case| {
5957
case.add_product(reflection::Product::new(
6058
"var",
@@ -64,32 +62,11 @@ where
6462
}
6563
}
6664

67-
impl<'a, T> Predicate<T> for EqPredicate<&'a T>
68-
where
69-
T: fmt::Debug + PartialEq + ?Sized,
70-
{
71-
fn eval(&self, variable: &T) -> bool {
72-
match self.op {
73-
EqOps::Equal => variable.eq(self.constant),
74-
EqOps::NotEqual => variable.ne(self.constant),
75-
}
76-
}
77-
78-
fn find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>> {
79-
utils::default_find_case(self, expected, variable).map(|case| {
80-
case.add_product(reflection::Product::new(
81-
"var",
82-
utils::DebugAdapter::new(variable).to_string(),
83-
))
84-
})
85-
}
86-
}
87-
88-
impl<T> reflection::PredicateReflection for EqPredicate<T> where T: fmt::Debug + PartialEq {}
65+
impl<T> reflection::PredicateReflection for EqPredicate<T> where T: fmt::Debug {}
8966

9067
impl<T> fmt::Display for EqPredicate<T>
9168
where
92-
T: fmt::Debug + PartialEq,
69+
T: fmt::Debug,
9370
{
9471
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
9572
let palette = crate::Palette::current();
@@ -120,6 +97,10 @@ where
12097
/// let predicate_fn = predicate::eq("Hello");
12198
/// assert_eq!(true, predicate_fn.eval("Hello"));
12299
/// assert_eq!(false, predicate_fn.eval("Goodbye"));
100+
///
101+
/// let predicate_fn = predicate::eq(String::from("Hello"));
102+
/// assert_eq!(true, predicate_fn.eval("Hello"));
103+
/// assert_eq!(false, predicate_fn.eval("Goodbye"));
123104
/// ```
124105
pub fn eq<T>(constant: T) -> EqPredicate<T>
125106
where
@@ -178,28 +159,26 @@ impl fmt::Display for OrdOps {
178159
///
179160
/// This is created by the `predicate::{gt, ge, lt, le}` functions.
180161
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
181-
pub struct OrdPredicate<T>
182-
where
183-
T: fmt::Debug + PartialOrd,
184-
{
162+
pub struct OrdPredicate<T> {
185163
constant: T,
186164
op: OrdOps,
187165
}
188166

189-
impl<T> Predicate<T> for OrdPredicate<T>
167+
impl<P, T> Predicate<P> for OrdPredicate<T>
190168
where
191-
T: fmt::Debug + PartialOrd,
169+
T: std::borrow::Borrow<P> + fmt::Debug,
170+
P: fmt::Debug + PartialOrd + ?Sized,
192171
{
193-
fn eval(&self, variable: &T) -> bool {
172+
fn eval(&self, variable: &P) -> bool {
194173
match self.op {
195-
OrdOps::LessThan => variable.lt(&self.constant),
196-
OrdOps::LessThanOrEqual => variable.le(&self.constant),
197-
OrdOps::GreaterThanOrEqual => variable.ge(&self.constant),
198-
OrdOps::GreaterThan => variable.gt(&self.constant),
174+
OrdOps::LessThan => variable.lt(self.constant.borrow()),
175+
OrdOps::LessThanOrEqual => variable.le(self.constant.borrow()),
176+
OrdOps::GreaterThanOrEqual => variable.ge(self.constant.borrow()),
177+
OrdOps::GreaterThan => variable.gt(self.constant.borrow()),
199178
}
200179
}
201180

202-
fn find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>> {
181+
fn find_case<'a>(&'a self, expected: bool, variable: &P) -> Option<reflection::Case<'a>> {
203182
utils::default_find_case(self, expected, variable).map(|case| {
204183
case.add_product(reflection::Product::new(
205184
"var",
@@ -209,34 +188,11 @@ where
209188
}
210189
}
211190

212-
impl<'a, T> Predicate<T> for OrdPredicate<&'a T>
213-
where
214-
T: fmt::Debug + PartialOrd + ?Sized,
215-
{
216-
fn eval(&self, variable: &T) -> bool {
217-
match self.op {
218-
OrdOps::LessThan => variable.lt(self.constant),
219-
OrdOps::LessThanOrEqual => variable.le(self.constant),
220-
OrdOps::GreaterThanOrEqual => variable.ge(self.constant),
221-
OrdOps::GreaterThan => variable.gt(self.constant),
222-
}
223-
}
224-
225-
fn find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>> {
226-
utils::default_find_case(self, expected, variable).map(|case| {
227-
case.add_product(reflection::Product::new(
228-
"var",
229-
utils::DebugAdapter::new(variable).to_string(),
230-
))
231-
})
232-
}
233-
}
234-
235-
impl<T> reflection::PredicateReflection for OrdPredicate<T> where T: fmt::Debug + PartialOrd {}
191+
impl<T> reflection::PredicateReflection for OrdPredicate<T> where T: fmt::Debug {}
236192

237193
impl<T> fmt::Display for OrdPredicate<T>
238194
where
239-
T: fmt::Debug + PartialOrd,
195+
T: fmt::Debug,
240196
{
241197
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242198
let palette = crate::Palette::current();
@@ -267,6 +223,10 @@ where
267223
/// let predicate_fn = predicate::lt("b");
268224
/// assert_eq!(true, predicate_fn.eval("a"));
269225
/// assert_eq!(false, predicate_fn.eval("c"));
226+
///
227+
/// let predicate_fn = predicate::lt(String::from("b"));
228+
/// assert_eq!(true, predicate_fn.eval("a"));
229+
/// assert_eq!(false, predicate_fn.eval("c"));
270230
/// ```
271231
pub fn lt<T>(constant: T) -> OrdPredicate<T>
272232
where

0 commit comments

Comments
 (0)