Skip to main content

predicates/
function.rs

1// Copyright (c) 2018 The predicates-rs Project Developers.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/license/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Definition of `Predicate` for wrapping a `Fn(&T) -> bool`
10
11use std::fmt;
12use std::marker::PhantomData;
13
14use crate::reflection;
15use crate::utils;
16use crate::Predicate;
17
18/// Predicate that wraps a function over a reference that returns a `bool`.
19/// This type is returned by the `predicate::function` function.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct FnPredicate<F, T>
22where
23    F: Fn(&T) -> bool,
24    T: ?Sized,
25{
26    function: F,
27    name: &'static str,
28    _phantom: PhantomData<T>,
29}
30
31unsafe impl<F, T> Send for FnPredicate<F, T>
32where
33    F: Send + Fn(&T) -> bool,
34    T: ?Sized,
35{
36}
37
38unsafe impl<F, T> Sync for FnPredicate<F, T>
39where
40    F: Sync + Fn(&T) -> bool,
41    T: ?Sized,
42{
43}
44
45impl<F, T> FnPredicate<F, T>
46where
47    F: Fn(&T) -> bool,
48    T: ?Sized,
49{
50    /// Provide a descriptive name for this function.
51    ///
52    /// # Examples
53    ///
54    /// ```
55    /// use predicates::prelude::*;
56    ///
57    /// struct Example {
58    ///     string: String,
59    ///     number: i32,
60    /// }
61    ///
62    /// let string_check = predicate::function(|x: &Example| x.string == "hello")
63    ///     .fn_name("is_hello");
64    /// println!("predicate: {}", string_check);
65    /// ```
66    pub fn fn_name(mut self, name: &'static str) -> Self {
67        self.name = name;
68        self
69    }
70}
71
72impl<F, T> Predicate<T> for FnPredicate<F, T>
73where
74    F: Fn(&T) -> bool,
75    T: ?Sized,
76{
77    fn eval(&self, variable: &T) -> bool {
78        (self.function)(variable)
79    }
80
81    fn find_case<'a>(&'a self, expected: bool, variable: &T) -> Option<reflection::Case<'a>> {
82        utils::default_find_case(self, expected, variable)
83    }
84}
85
86impl<F, T> reflection::PredicateReflection for FnPredicate<F, T>
87where
88    F: Fn(&T) -> bool,
89    T: ?Sized,
90{
91}
92
93impl<F, T> fmt::Display for FnPredicate<F, T>
94where
95    F: Fn(&T) -> bool,
96    T: ?Sized,
97{
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        let palette = crate::Palette::current();
100        write!(
101            f,
102            "{}({})",
103            palette.description.paint(self.name),
104            palette.var.paint("var"),
105        )
106    }
107}
108
109/// Creates a new predicate that wraps over the given function. The returned
110/// type implements `Predicate` and therefore has all combinators available to
111/// it.
112///
113/// # Examples
114///
115/// ```
116/// use predicates::prelude::*;
117///
118/// struct Example {
119///     string: String,
120///     number: i32,
121/// }
122///
123/// let string_check = predicate::function(|x: &Example| x.string == "hello");
124/// let number_check = predicate::function(|x: &Example| x.number == 42);
125/// let predicate_fn = string_check.and(number_check);
126/// let good_example = Example { string: "hello".into(), number: 42 };
127/// assert_eq!(true, predicate_fn.eval(&good_example));
128/// let bad_example = Example { string: "goodbye".into(), number: 0 };
129/// assert_eq!(false, predicate_fn.eval(&bad_example));
130/// ```
131pub fn function<F, T>(function: F) -> FnPredicate<F, T>
132where
133    F: Fn(&T) -> bool,
134    T: ?Sized,
135{
136    FnPredicate {
137        function,
138        name: "fn",
139        _phantom: PhantomData,
140    }
141}
142
143#[test]
144fn str_function() {
145    let f = function(|x: &str| x == "hello");
146    assert!(f.eval("hello"));
147    assert!(!f.eval("goodbye"));
148}