1pub trait SliceExt {
4 fn trim(&self) -> &Self;
5 fn trim_trailing(&self) -> &Self;
6}
7
8fn is_whitespace(c: &u8) -> bool {
9 *c == b'\t' || *c == b' '
10}
11
12fn is_not_whitespace(c: &u8) -> bool {
13 !is_whitespace(c)
14}
15
16impl SliceExt for [u8] {
17 fn trim(&self) -> &[u8] {
19 if let Some(first) = self.iter().position(is_not_whitespace) {
20 if let Some(last) = self.iter().rposition(is_not_whitespace) {
21 &self[first..last + 1]
22 } else {
23 unreachable!();
24 }
25 } else {
26 &[]
27 }
28 }
29
30 fn trim_trailing(&self) -> &[u8] {
32 if let Some(last) = self.iter().rposition(is_not_whitespace) {
33 &self[0..last + 1]
34 } else {
35 &[]
36 }
37 }
38}
39
40fn buf_trim_trailing(buf: &[u8]) -> Vec<&[u8]> {
42 let trimmed_lines: Vec<&[u8]> = buf
43 .split(|c| *c == b'\n')
44 .map(SliceExt::trim_trailing) .collect();
46
47 trimmed_lines
48}
49
50fn drop_last_empty_lines<'a>(lines: &[&'a [u8]]) -> Vec<&'a [u8]> {
52 if let Some(last) = lines.iter().rposition(|line| !line.is_empty()) {
53 lines[0..=last].to_vec()
54 } else {
55 lines.to_vec()
56 }
57}
58
59pub fn cleanup_captured_buffer(buffer: &[u8], drop_n_last_lines: usize) -> Vec<u8> {
70 let trimmed_lines: Vec<&[u8]> = buf_trim_trailing(buffer);
71 let mut buffer: Vec<&[u8]> = drop_last_empty_lines(&trimmed_lines);
72 buffer.truncate(buffer.len() - drop_n_last_lines);
73
74 let mut final_buffer: Vec<u8> = Vec::with_capacity(buffer.len());
76 for (idx, &line) in buffer.iter().enumerate() {
77 final_buffer.extend_from_slice(line);
78
79 let is_last_line = idx == buffer.len() - 1;
80 if is_last_line {
81 let reset = "\u{001b}[0m".as_bytes();
82 final_buffer.extend_from_slice(reset);
83 final_buffer.push(b'\n');
84 } else {
85 final_buffer.push(b'\n');
86 }
87 }
88
89 final_buffer
90}
91
92#[cfg(test)]
93mod tests {
94 use super::{buf_trim_trailing, drop_last_empty_lines, SliceExt};
95
96 #[test]
97 fn trims_trailing_whitespaces() {
98 let input = " text ".as_bytes();
99 let expected = " text".as_bytes();
100
101 let actual = input.trim_trailing();
102 assert_eq!(actual, expected);
103 }
104
105 #[test]
106 fn trims_whitespaces() {
107 let input = " text ".as_bytes();
108 let expected = "text".as_bytes();
109
110 let actual = input.trim();
111 assert_eq!(actual, expected);
112 }
113
114 #[test]
115 fn test_buf_trim_trailing() {
116 let text = "line1\n\nline3 ";
117 let actual = buf_trim_trailing(text.as_bytes());
118 let expected = vec!["line1".as_bytes(), "".as_bytes(), "line3".as_bytes()];
119 assert_eq!(actual, expected);
120 }
121
122 #[test]
123 fn test_buf_drop_last_empty_lines() {
124 let text = "line1\nline2\n\nline3 ";
125
126 let trimmed_lines = buf_trim_trailing(text.as_bytes());
127 let actual = drop_last_empty_lines(&trimmed_lines);
128 let expected = trimmed_lines;
129 assert_eq!(actual, expected);
130
131 let text = "line1\nline2\n\n\n ";
134
135 let trimmed_lines = buf_trim_trailing(text.as_bytes());
136 let actual = drop_last_empty_lines(&trimmed_lines);
137 let expected = vec!["line1".as_bytes(), "line2".as_bytes()];
138 assert_eq!(actual, expected);
139 }
140}