Skip to content

Commit

Permalink
feat: Allow into_iter predicates to own object and eval vs borrowed t…
Browse files Browse the repository at this point in the history
…ypes

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 In/OrdIn/HashInPredicate that store
an object that implements Borrow for the predicate type, replacing
existing impls of Predicate<T> for In/OrdIn/HashInPredicate<T> and
In/OrdIn/HashInPredicate<&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
InPredicate<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 #20 more completely.
  • Loading branch information
rshearman committed Dec 29, 2022
1 parent f9536e0 commit 7934a3a
Showing 1 changed file with 35 additions and 68 deletions.
103 changes: 35 additions & 68 deletions src/iter.rs
Expand Up @@ -60,6 +60,11 @@ where
/// assert_eq!(true, predicate_fn.eval("a"));
/// assert_eq!(false, predicate_fn.eval("b"));
/// assert_eq!(true, predicate_fn.eval("c"));
///
/// let predicate_fn = predicate::in_iter(vec![String::from("a"), String::from("c"), String::from("e")]).sort();
/// assert_eq!(true, predicate_fn.eval("a"));
/// assert_eq!(false, predicate_fn.eval("b"));
/// assert_eq!(true, predicate_fn.eval("c"));
/// ```
pub fn sort(self) -> OrdInPredicate<T> {
let mut items = self.inner.debug;
Expand All @@ -70,33 +75,16 @@ where
}
}

impl<T> Predicate<T> for InPredicate<T>
where
T: PartialEq + fmt::Debug,
{
fn eval(&self, variable: &T) -> bool {
self.inner.debug.contains(variable)
}

fn find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>> {
utils::default_find_case(self, expected, variable).map(|case| {
case.add_product(reflection::Product::new(
"var",
utils::DebugAdapter::new(variable).to_string(),
))
})
}
}

impl<'a, T> Predicate<T> for InPredicate<&'a T>
impl<P, T> Predicate<P> for InPredicate<T>
where
T: PartialEq + fmt::Debug + ?Sized,
T: std::borrow::Borrow<P> + PartialEq + fmt::Debug,
P: PartialEq + fmt::Debug + ?Sized,
{
fn eval(&self, variable: &T) -> bool {
self.inner.debug.contains(&variable)
fn eval(&self, variable: &P) -> bool {
self.inner.debug.iter().any(|x| x.borrow() == variable)
}

fn find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>> {
fn find_case<'a>(&'a self, expected: bool, variable: &P) -> Option<reflection::Case<'a>> {
utils::default_find_case(self, expected, variable).map(|case| {
case.add_product(reflection::Product::new(
"var",
Expand Down Expand Up @@ -160,6 +148,11 @@ where
/// assert_eq!(true, predicate_fn.eval("a"));
/// assert_eq!(false, predicate_fn.eval("b"));
/// assert_eq!(true, predicate_fn.eval("c"));
///
/// let predicate_fn = predicate::in_iter(vec![String::from("a"), String::from("c"), String::from("e")]);
/// assert_eq!(true, predicate_fn.eval("a"));
/// assert_eq!(false, predicate_fn.eval("b"));
/// assert_eq!(true, predicate_fn.eval("c"));
/// ```
pub fn in_iter<I, T>(iter: I) -> InPredicate<T>
where
Expand Down Expand Up @@ -188,33 +181,19 @@ where
inner: utils::DebugAdapter<Vec<T>>,
}

impl<T> Predicate<T> for OrdInPredicate<T>
impl<P, T> Predicate<P> for OrdInPredicate<T>
where
T: Ord + fmt::Debug,
T: std::borrow::Borrow<P> + Ord + fmt::Debug,
P: Ord + fmt::Debug + ?Sized,
{
fn eval(&self, variable: &T) -> bool {
self.inner.debug.binary_search(variable).is_ok()
fn eval(&self, variable: &P) -> bool {
self.inner
.debug
.binary_search_by(|x| x.borrow().cmp(variable))
.is_ok()
}

fn find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>> {
utils::default_find_case(self, expected, variable).map(|case| {
case.add_product(reflection::Product::new(
"var",
utils::DebugAdapter::new(variable).to_string(),
))
})
}
}

impl<'a, T> Predicate<T> for OrdInPredicate<&'a T>
where
T: Ord + fmt::Debug + ?Sized,
{
fn eval(&self, variable: &T) -> bool {
self.inner.debug.binary_search(&variable).is_ok()
}

fn find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>> {
fn find_case<'a>(&'a self, expected: bool, variable: &P) -> Option<reflection::Case<'a>> {
utils::default_find_case(self, expected, variable).map(|case| {
case.add_product(reflection::Product::new(
"var",
Expand Down Expand Up @@ -267,33 +246,16 @@ where
inner: utils::DebugAdapter<HashSet<T>>,
}

impl<T> Predicate<T> for HashableInPredicate<T>
impl<P, T> Predicate<P> for HashableInPredicate<T>
where
T: Hash + Eq + fmt::Debug,
T: std::borrow::Borrow<P> + Hash + Eq + fmt::Debug,
P: Hash + Eq + fmt::Debug + ?Sized,
{
fn eval(&self, variable: &T) -> bool {
fn eval(&self, variable: &P) -> bool {
self.inner.debug.contains(variable)
}

fn find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>> {
utils::default_find_case(self, expected, variable).map(|case| {
case.add_product(reflection::Product::new(
"var",
utils::DebugAdapter::new(variable).to_string(),
))
})
}
}

impl<'a, T> Predicate<T> for HashableInPredicate<&'a T>
where
T: Hash + Eq + fmt::Debug + ?Sized,
{
fn eval(&self, variable: &T) -> bool {
self.inner.debug.contains(&variable)
}

fn find_case<'b>(&'b self, expected: bool, variable: &T) -> Option<reflection::Case<'b>> {
fn find_case<'a>(&'a self, expected: bool, variable: &P) -> Option<reflection::Case<'a>> {
utils::default_find_case(self, expected, variable).map(|case| {
case.add_product(reflection::Product::new(
"var",
Expand Down Expand Up @@ -351,6 +313,11 @@ where
/// assert_eq!(true, predicate_fn.eval("a"));
/// assert_eq!(false, predicate_fn.eval("b"));
/// assert_eq!(true, predicate_fn.eval("c"));
///
/// let predicate_fn = predicate::in_hash(vec![String::from("a"), String::from("c"), String::from("e")]);
/// assert_eq!(true, predicate_fn.eval("a"));
/// assert_eq!(false, predicate_fn.eval("b"));
/// assert_eq!(true, predicate_fn.eval("c"));
/// ```
pub fn in_hash<I, T>(iter: I) -> HashableInPredicate<T>
where
Expand Down

0 comments on commit 7934a3a

Please sign in to comment.