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