1use std::mem::take;
28use textwrap::wrap;
29
30const CLIPPY_ART: &str = r#"/‾‾\
31| |
32@ @
33|| |/
34|| ||
35|\_/|
36\___/
37 /\
38"#;
39
40#[derive(Default, Clone, PartialOrd, PartialEq)]
45pub struct ClippyOutput {
46 buf: String,
47
48 output_width: u16,
50
51 line: String,
53
54 line_char_length: u16,
58}
59
60impl ClippyOutput {
61 pub fn new(mut output_width: u16) -> Self {
62 if output_width < 5 {
63 output_width = 5;
64 }
65
66 let mut s = CLIPPY_ART.to_string() + "/‾ ";
67 for _ in 0..output_width - 5 {
68 s.push('‾');
69 }
70 s.push_str("\\\n");
71 Self {
72 buf: s,
73 output_width,
74 line: String::new(),
75 line_char_length: 0,
76 }
77 }
78
79 pub fn add_str(&mut self, s: &str) {
81 let lines = wrap(s, usize::min((self.output_width - 4) as usize, 2000));
82
83 for (i, line) in lines.iter().enumerate() {
84 for char in line.chars() {
85 if char == '\n' {
86 ClippyOutput::add_string_to_buffer(
87 &mut self.buf,
88 &self.line,
89 self.output_width - 4 - self.line_char_length,
90 );
91 self.line.clear();
92 self.line_char_length = 0;
93 } else {
94 self.line.push(char);
95 self.line_char_length += 1;
96 }
97
98 if self.line_char_length == self.output_width - 4 {
99 ClippyOutput::add_string_to_buffer(&mut self.buf, &self.line, 0);
100 self.line.clear();
101 self.line_char_length = 0;
102 }
103 }
104
105 if i < lines.len() - 1 {
106 ClippyOutput::add_string_to_buffer(
107 &mut self.buf,
108 &self.line,
109 self.output_width - 4 - self.line_char_length,
110 );
111 self.line.clear();
112 self.line_char_length = 0;
113 }
114 }
115 }
116
117 fn add_string_to_buffer(buf: &mut String, line: &str, space_count: u16) {
118 if !line.is_empty() {
119 buf.push_str("| ");
120 buf.push_str(&line);
121 for _ in 0..space_count {
122 buf.push(' ');
123 }
124 buf.push_str(" |\n");
125 }
126 }
127
128 pub fn finish(&mut self) {
132 if !self.line.is_empty() {
133 ClippyOutput::add_string_to_buffer(
134 &mut self.buf,
135 &self.line,
136 self.output_width - 4 - self.line.chars().count() as u16,
137 );
138 self.line.clear();
139 self.line_char_length = 0;
140 }
141
142 self.buf.push('\\');
143 for _ in 0..self.output_width - 2 {
144 self.buf.push('_');
145 }
146 self.buf.push_str("/\n");
147 }
148}
149
150impl Iterator for ClippyOutput {
151 type Item = String;
152
153 fn next(&mut self) -> Option<String> {
156 let result = take(&mut self.buf);
157 if result.is_empty() {
158 None
159 } else {
160 Some(result)
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn clippy_output() {
171 {
172 let mut clippy = ClippyOutput::new(0);
174 let result: String = clippy.by_ref().collect();
175 assert_eq!(result, CLIPPY_ART.to_string() + "/‾ \\\n");
176
177 clippy.finish();
178 let result: String = clippy.by_ref().collect();
179 assert_eq!(result, "\\___/\n");
180 }
181 {
182 let mut clippy = ClippyOutput::new(0);
184 clippy.add_str("");
185 clippy.finish();
186 let result: String = clippy.collect();
187 assert_eq!(result, CLIPPY_ART.to_string() + "/‾ \\\n\\___/\n");
188 }
189 {
190 let mut clippy = ClippyOutput::new(0);
191 clippy.add_str("a");
192 let result: String = clippy.by_ref().collect();
193 assert_eq!(result, CLIPPY_ART.to_string() + "/‾ \\\n| a |\n");
194
195 clippy.finish();
196 let result: String = clippy.by_ref().collect();
197 assert_eq!(result, "\\___/\n");
198
199 clippy.add_str("a");
201 clippy.finish();
202 let result: String = clippy.collect();
203 assert_eq!(result, "| a |\n\\___/\n");
204 }
205 {
206 let mut clippy = ClippyOutput::new(6);
208 clippy.add_str("aa");
209 clippy.finish();
210
211 let result: String = clippy.collect();
212 assert_eq!(
213 result,
214 CLIPPY_ART.to_string() + "/‾ ‾\\\n| aa |\n\\____/\n"
215 );
216 }
217 {
218 let mut clippy = ClippyOutput::new(6);
220 clippy.add_str("aaa");
221 clippy.finish();
222
223 let result: String = clippy.collect();
224 assert_eq!(
225 result,
226 CLIPPY_ART.to_string() + "/‾ ‾\\\n| aa |\n| a |\n\\____/\n"
227 );
228 }
229 {
230 let mut clippy = ClippyOutput::new(6);
232 clippy.add_str("a");
233 let result: String = clippy.by_ref().collect();
234 assert_eq!(result, CLIPPY_ART.to_string() + "/‾ ‾\\\n");
235
236 clippy.add_str("a");
237 clippy.finish();
238 let result: String = clippy.collect();
239 assert_eq!(result, "| aa |\n\\____/\n");
240 }
241 {
242 let mut clippy = ClippyOutput::new(6);
244 clippy.add_str("a\n");
245 let result: String = clippy.by_ref().collect();
246 assert_eq!(result, CLIPPY_ART.to_string() + "/‾ ‾\\\n| a |\n");
247
248 clippy.add_str("b");
249 clippy.finish();
250 let result: String = clippy.collect();
251 assert_eq!(result, "| b |\n\\____/\n");
252 }
253 {
254 let mut clippy = ClippyOutput::new(7);
256 clippy.add_str("a");
257 clippy.add_str("\n");
258 clippy.add_str("aaaa\n");
259 clippy.add_str("b");
260 clippy.finish();
261 let result: String = clippy.collect();
262 assert_eq!(
263 result,
264 CLIPPY_ART.to_string() + "/‾ ‾‾\\\n| a |\n| aaa |\n| a |\n| b |\n\\_____/\n"
265 );
266 }
267 }
268}