1use regex::Regex;
2use smallvec::*;
3use std::io::{Result, Write};
4use textwrap::fill;
5use unicode_width::UnicodeWidthStr;
6
7const MASCOT: &[u8] = if cfg!(feature = "clippy") {
8 br#"
9 \
10 \
11 __
12 / \
13 | |
14 @ @
15 | |
16 || |/
17 || ||
18 |\_/|
19 \___/
20"#
21} else {
22 br#"
23 \
24 \
25 _~^~^~_
26 \) / o o \ (/
27 '_ - _'
28 / '-----' \
29"#
30};
31
32const BUFSIZE: usize = 2048;
35
36pub fn say<W>(input: &str, max_width: usize, mut writer: W) -> Result<()>
75where
76 W: Write,
77{
78 let mut write_buffer = SmallVec::<[u8; BUFSIZE]>::new();
80
81 let input = merge_white_spaces(input);
83
84 let wrapped = fill(input.as_str(), max_width);
86
87 let lines: Vec<&str> = wrapped.lines().collect();
88
89 let line_count = lines.len();
90 let actual_width = longest_line(&lines);
91
92 write_buffer.push(b' ');
94 for _ in 0..(actual_width + 2) {
95 write_buffer.push(b'_');
96 }
97 write_buffer.push(b'\n');
98
99 for (i, line) in lines.into_iter().enumerate() {
101 if line_count == 1 {
102 write_buffer.extend_from_slice(b"< ");
103 } else if i == 0 {
104 write_buffer.extend_from_slice(b"/ ");
105 } else if i == line_count - 1 {
106 write_buffer.extend_from_slice(b"\\ ");
107 } else {
108 write_buffer.extend_from_slice(b"| ");
109 }
110
111 let line_len = UnicodeWidthStr::width(line);
112 write_buffer.extend_from_slice(line.as_bytes());
113 for _ in line_len..actual_width {
114 write_buffer.push(b' ');
115 }
116
117 if line_count == 1 {
118 write_buffer.extend_from_slice(b" >\n");
119 } else if i == 0 {
120 write_buffer.extend_from_slice(b" \\\n");
121 } else if i == line_count - 1 {
122 write_buffer.extend_from_slice(b" /\n");
123 } else {
124 write_buffer.extend_from_slice(b" |\n");
125 }
126 }
127
128 write_buffer.push(b' ');
130 for _ in 0..(actual_width + 2) {
131 write_buffer.push(b'-');
132 }
133
134 write_buffer.extend_from_slice(MASCOT);
136
137 writer.write_all(&write_buffer)
138}
139
140fn longest_line(lines: &[&str]) -> usize {
141 lines
142 .iter()
143 .map(|line| UnicodeWidthStr::width(*line))
144 .max()
145 .unwrap_or(0)
146}
147
148fn merge_white_spaces(input: &str) -> String {
150 let re = Regex::new(r"([^\S\r\n])+").unwrap();
151 re.replace_all(input, " ").to_string()
152}