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