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 | use std::ffi; |
10 | use std::fmt; |
11 | use std::str; |
12 | |
13 | use crate::reflection; |
14 | #[cfg (feature = "normalize-line-endings" )] |
15 | use crate::str::normalize::NormalizedPredicate; |
16 | use crate::Predicate; |
17 | |
18 | /// Predicate adaper that trims the variable being tested. |
19 | /// |
20 | /// This is created by `pred.trim()`. |
21 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
22 | pub struct TrimPredicate<P> |
23 | where |
24 | P: Predicate<str>, |
25 | { |
26 | p: P, |
27 | } |
28 | |
29 | impl<P> Predicate<str> for TrimPredicate<P> |
30 | where |
31 | P: Predicate<str>, |
32 | { |
33 | fn eval(&self, variable: &str) -> bool { |
34 | self.p.eval(variable.trim()) |
35 | } |
36 | |
37 | fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>> { |
38 | self.p.find_case(expected, variable.trim()) |
39 | } |
40 | } |
41 | |
42 | impl<P> reflection::PredicateReflection for TrimPredicate<P> |
43 | where |
44 | P: Predicate<str>, |
45 | { |
46 | fn children<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Child<'a>> + 'a> { |
47 | let params = vec![reflection::Child::new("predicate" , &self.p)]; |
48 | Box::new(params.into_iter()) |
49 | } |
50 | } |
51 | |
52 | impl<P> fmt::Display for TrimPredicate<P> |
53 | where |
54 | P: Predicate<str>, |
55 | { |
56 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
57 | self.p.fmt(f) |
58 | } |
59 | } |
60 | |
61 | /// Predicate adaper that converts a `str` predicate to byte predicate. |
62 | /// |
63 | /// This is created by `pred.from_utf8()`. |
64 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
65 | pub struct Utf8Predicate<P> |
66 | where |
67 | P: Predicate<str>, |
68 | { |
69 | p: P, |
70 | } |
71 | |
72 | impl<P> Predicate<ffi::OsStr> for Utf8Predicate<P> |
73 | where |
74 | P: Predicate<str>, |
75 | { |
76 | fn eval(&self, variable: &ffi::OsStr) -> bool { |
77 | variable.to_str().map(|s| self.p.eval(s)).unwrap_or(false) |
78 | } |
79 | |
80 | fn find_case<'a>( |
81 | &'a self, |
82 | expected: bool, |
83 | variable: &ffi::OsStr, |
84 | ) -> Option<reflection::Case<'a>> { |
85 | let var_str = variable.to_str(); |
86 | match (expected, var_str) { |
87 | (_, Some(var_str)) => self.p.find_case(expected, var_str).map(|child| { |
88 | child.add_product(reflection::Product::new("var as str" , var_str.to_owned())) |
89 | }), |
90 | (true, None) => None, |
91 | (false, None) => Some( |
92 | reflection::Case::new(Some(self), false) |
93 | .add_product(reflection::Product::new("error" , "Invalid UTF-8 string" )), |
94 | ), |
95 | } |
96 | } |
97 | } |
98 | |
99 | impl<P> Predicate<[u8]> for Utf8Predicate<P> |
100 | where |
101 | P: Predicate<str>, |
102 | { |
103 | fn eval(&self, variable: &[u8]) -> bool { |
104 | str::from_utf8(variable) |
105 | .map(|s| self.p.eval(s)) |
106 | .unwrap_or(false) |
107 | } |
108 | |
109 | fn find_case<'a>(&'a self, expected: bool, variable: &[u8]) -> Option<reflection::Case<'a>> { |
110 | let var_str = str::from_utf8(variable); |
111 | match (expected, var_str) { |
112 | (_, Ok(var_str)) => self.p.find_case(expected, var_str).map(|child| { |
113 | child.add_product(reflection::Product::new("var as str" , var_str.to_owned())) |
114 | }), |
115 | (true, Err(_)) => None, |
116 | (false, Err(err)) => Some( |
117 | reflection::Case::new(Some(self), false) |
118 | .add_product(reflection::Product::new("error" , err)), |
119 | ), |
120 | } |
121 | } |
122 | } |
123 | |
124 | impl<P> reflection::PredicateReflection for Utf8Predicate<P> |
125 | where |
126 | P: Predicate<str>, |
127 | { |
128 | fn children<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Child<'a>> + 'a> { |
129 | let params = vec![reflection::Child::new("predicate" , &self.p)]; |
130 | Box::new(params.into_iter()) |
131 | } |
132 | } |
133 | |
134 | impl<P> fmt::Display for Utf8Predicate<P> |
135 | where |
136 | P: Predicate<str>, |
137 | { |
138 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
139 | self.p.fmt(f) |
140 | } |
141 | } |
142 | |
143 | /// `Predicate` extension adapting a `str` Predicate. |
144 | pub trait PredicateStrExt |
145 | where |
146 | Self: Predicate<str>, |
147 | Self: Sized, |
148 | { |
149 | /// Returns a `TrimPredicate` that ensures the data passed to `Self` is trimmed. |
150 | /// |
151 | /// # Examples |
152 | /// |
153 | /// ``` |
154 | /// use predicates::prelude::*; |
155 | /// |
156 | /// let predicate_fn = predicate::str::is_empty().trim(); |
157 | /// assert_eq!(true, predicate_fn.eval(" " )); |
158 | /// assert_eq!(false, predicate_fn.eval(" Hello " )); |
159 | /// ``` |
160 | fn trim(self) -> TrimPredicate<Self> { |
161 | TrimPredicate { p: self } |
162 | } |
163 | |
164 | /// Returns a `Utf8Predicate` that adapts `Self` to a `[u8]` `Predicate`. |
165 | /// |
166 | /// # Examples |
167 | /// |
168 | /// ``` |
169 | /// use predicates::prelude::*; |
170 | /// use std::ffi::OsStr; |
171 | /// |
172 | /// let predicate_fn = predicate::str::is_empty().not().from_utf8(); |
173 | /// assert_eq!(true, predicate_fn.eval(OsStr::new("Hello" ))); |
174 | /// assert_eq!(false, predicate_fn.eval(OsStr::new("" ))); |
175 | /// let variable: &[u8] = b"" ; |
176 | /// assert_eq!(false, predicate_fn.eval(variable)); |
177 | /// ``` |
178 | #[allow (clippy::wrong_self_convention)] |
179 | fn from_utf8(self) -> Utf8Predicate<Self> { |
180 | Utf8Predicate { p: self } |
181 | } |
182 | |
183 | /// Returns a `NormalizedPredicate` that ensures |
184 | /// the newlines within the data passed to `Self` is normalised. |
185 | /// |
186 | /// # Examples |
187 | /// |
188 | /// ``` |
189 | /// use predicates::prelude::*; |
190 | /// |
191 | /// let predicate_fn = predicate::eq("Hello World! \n" ).normalize(); |
192 | /// assert_eq!(true, predicate_fn.eval("Hello World! \n" )); |
193 | /// assert_eq!(true, predicate_fn.eval("Hello World! \r" )); |
194 | /// assert_eq!(true, predicate_fn.eval("Hello World! \r\n" )); |
195 | /// assert_eq!(false, predicate_fn.eval("Goodbye" )); |
196 | /// ``` |
197 | /// |
198 | #[cfg (feature = "normalize-line-endings" )] |
199 | fn normalize(self) -> NormalizedPredicate<Self> { |
200 | NormalizedPredicate { p: self } |
201 | } |
202 | } |
203 | |
204 | impl<P> PredicateStrExt for P where P: Predicate<str> {} |
205 | |