1 //! Various extensions to Rust std types. 2 3 pub(crate) trait AsciiExt { is_whitespace(self) -> bool4 fn is_whitespace(self) -> bool; is_not_line_ending_whitespace(self) -> bool5 fn is_not_line_ending_whitespace(self) -> bool; is_line_ending_whitespace(self) -> bool6 fn is_line_ending_whitespace(self) -> bool; 7 } 8 9 impl AsciiExt for u8 { is_whitespace(self) -> bool10 fn is_whitespace(self) -> bool { 11 self == b' ' || (b'\x09'..=b'\x0d').contains(&self) 12 } 13 is_not_line_ending_whitespace(self) -> bool14 fn is_not_line_ending_whitespace(self) -> bool { 15 self.is_whitespace() && !self.is_line_ending_whitespace() 16 } 17 is_line_ending_whitespace(self) -> bool18 fn is_line_ending_whitespace(self) -> bool { 19 self == b'\n' 20 } 21 } 22 23 pub(crate) trait SliceExt { trim_first_and_last_line_of_whitespace(&self) -> &Self24 fn trim_first_and_last_line_of_whitespace(&self) -> &Self; trim_start(&self) -> &Self25 fn trim_start(&self) -> &Self; trim(&self) -> &Self26 fn trim(&self) -> &Self; contains_slice(&self, needle: &Self) -> bool27 fn contains_slice(&self, needle: &Self) -> bool; 28 } 29 30 impl SliceExt for [u8] { trim_first_and_last_line_of_whitespace(&self) -> &Self31 fn trim_first_and_last_line_of_whitespace(&self) -> &Self { 32 let start = self 33 .iter() 34 .position(|c| c.is_line_ending_whitespace() || !c.is_whitespace()) 35 .map_or(0, |i| (i + 1).min(self.len().saturating_sub(1))); 36 37 let end = self 38 .iter() 39 .rposition(|c| c.is_line_ending_whitespace() || !c.is_whitespace()) 40 .map_or_else( 41 || self.len(), 42 |i| { 43 if self[i.saturating_sub(1)] == b'\r' { 44 i - 1 45 } else { 46 i 47 } 48 }, 49 ); 50 51 if self[start..].is_empty() { 52 return &[]; 53 } 54 55 &self[start..=end] 56 } 57 trim_start(&self) -> &Self58 fn trim_start(&self) -> &Self { 59 let length = self.len(); 60 61 if length == 0 { 62 return &self; 63 } 64 65 let start = match self.iter().position(|c| !c.is_whitespace()) { 66 Some(start) => start, 67 None => return &[], 68 }; 69 70 &self[start..] 71 } 72 trim(&self) -> &Self73 fn trim(&self) -> &Self { 74 let length = self.len(); 75 76 if length == 0 { 77 return &self; 78 } 79 80 let start = match self.iter().position(|c| !c.is_whitespace()) { 81 Some(start) => start, 82 None => return &[], 83 }; 84 85 let end = match self.iter().rposition(|c| !c.is_whitespace()) { 86 Some(end) => end.max(start), 87 _ => length, 88 }; 89 90 &self[start..=end] 91 } 92 contains_slice(&self, needle: &Self) -> bool93 fn contains_slice(&self, needle: &Self) -> bool { 94 let self_length = self.len(); 95 let needle_length = needle.len(); 96 97 if needle_length == 0 || needle_length > self_length { 98 return false; 99 } else if needle_length == self_length { 100 return self == needle; 101 } 102 103 for window in self.windows(needle_length) { 104 if needle == window { 105 return true; 106 } 107 } 108 109 false 110 } 111 } 112 113 #[cfg(test)] 114 mod tests { 115 use super::*; 116 117 #[test] is_whitespace()118 fn is_whitespace() { 119 assert!(b' '.is_whitespace()); 120 assert!(b'\r'.is_whitespace()); 121 assert!(b'\n'.is_whitespace()); 122 } 123 124 #[test] trim()125 fn trim() { 126 assert!([b' ', b' ', b' '].trim().is_empty()); 127 assert!([b' ', b'\r', b'\n'].trim().is_empty()); 128 assert!([b'\n'].trim().is_empty()); 129 assert!([].trim().is_empty()); 130 131 assert_eq!([b'a', b'b'], [b'a', b'b'].trim()); 132 assert_eq!([b'h', b'i'], [b' ', b'h', b'i'].trim()); 133 assert_eq!([b'h', b'i'], [b'h', b'i', b' '].trim()); 134 assert_eq!([b'h', b'i'], [b' ', b'h', b'i', b' '].trim()); 135 } 136 137 #[test] contains()138 fn contains() { 139 assert!([1, 2, 3, 4, 5].contains_slice(&[1, 2, 3, 4, 5])); 140 assert!([1, 2, 3, 4, 5].contains_slice(&[1, 2, 3])); 141 assert!([1, 2, 3, 4, 5].contains_slice(&[3, 4, 5])); 142 assert!([1, 2, 3, 4, 5].contains_slice(&[2, 3, 4])); 143 assert!(![1, 2, 3, 4, 5].contains_slice(&[])); 144 } 145 } 146