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 {
eval(&self, variable: &str) -> bool33     fn eval(&self, variable: &str) -> bool {
34         self.p.eval(variable.trim())
35     }
36 
find_case<'a>(&'a self, expected: bool, variable: &str) -> Option<reflection::Case<'a>>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 {
children<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Child<'a>> + 'a>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 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result56     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 {
eval(&self, variable: &ffi::OsStr) -> bool76     fn eval(&self, variable: &ffi::OsStr) -> bool {
77         variable.to_str().map(|s| self.p.eval(s)).unwrap_or(false)
78     }
79 
find_case<'a>( &'a self, expected: bool, variable: &ffi::OsStr, ) -> Option<reflection::Case<'a>>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 {
eval(&self, variable: &[u8]) -> bool103     fn eval(&self, variable: &[u8]) -> bool {
104         str::from_utf8(variable)
105             .map(|s| self.p.eval(s))
106             .unwrap_or(false)
107     }
108 
find_case<'a>(&'a self, expected: bool, variable: &[u8]) -> Option<reflection::Case<'a>>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 {
children<'a>(&'a self) -> Box<dyn Iterator<Item = reflection::Child<'a>> + 'a>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 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result138     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     /// ```
trim(self) -> TrimPredicate<Self>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)]
from_utf8(self) -> Utf8Predicate<Self>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")]
normalize(self) -> NormalizedPredicate<Self>199     fn normalize(self) -> NormalizedPredicate<Self> {
200         NormalizedPredicate { p: self }
201     }
202 }
203 
204 impl<P> PredicateStrExt for P where P: Predicate<str> {}
205