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 | |
11 | use std::fmt; |
12 | use std::marker::PhantomData; |
13 | |
14 | use crate::reflection; |
15 | use crate::utils; |
16 | use 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)] |
21 | pub struct FnPredicate<F, T> |
22 | where |
23 | F: Fn(&T) -> bool, |
24 | T: ?Sized, |
25 | { |
26 | function: F, |
27 | name: &'static str, |
28 | _phantom: PhantomData<T>, |
29 | } |
30 | |
31 | unsafe impl<F, T> Send for FnPredicate<F, T> |
32 | where |
33 | F: Send + Fn(&T) -> bool, |
34 | T: ?Sized, |
35 | { |
36 | } |
37 | |
38 | unsafe impl<F, T> Sync for FnPredicate<F, T> |
39 | where |
40 | F: Sync + Fn(&T) -> bool, |
41 | T: ?Sized, |
42 | { |
43 | } |
44 | |
45 | impl<F, T> FnPredicate<F, T> |
46 | where |
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 | |
72 | impl<F, T> Predicate<T> for FnPredicate<F, T> |
73 | where |
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 | |
86 | impl<F, T> reflection::PredicateReflection for FnPredicate<F, T> |
87 | where |
88 | F: Fn(&T) -> bool, |
89 | T: ?Sized, |
90 | { |
91 | } |
92 | |
93 | impl<F, T> fmt::Display for FnPredicate<F, T> |
94 | where |
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 | /// ``` |
131 | pub fn function<F, T>(function: F) -> FnPredicate<F, T> |
132 | where |
133 | F: Fn(&T) -> bool, |
134 | T: ?Sized, |
135 | { |
136 | FnPredicate { |
137 | function, |
138 | name: "fn" , |
139 | _phantom: PhantomData, |
140 | } |
141 | } |
142 | |
143 | #[test] |
144 | fn str_function() { |
145 | let f = function(|x: &str| x == "hello" ); |
146 | assert!(f.eval("hello" )); |
147 | assert!(!f.eval("goodbye" )); |
148 | } |
149 | |