1extern crate memchr;
33extern crate termcolor;
34
35use memchr::memchr;
36use termcolor::{Color, ColorSpec, WriteColor};
37
38use std::io;
39
40pub fn write_ansi<W: WriteColor>(mut writer: W, mut ansi: &[u8]) -> io::Result<()> {
44 while let Some(index) = memchr(0x1b, ansi) {
45 let (left, right) = ansi.split_at(index);
46 writer.write_all(left)?;
47 if right.is_empty() {
48 return Ok(());
49 }
50
51 let mut parser = ColorSpecParser::new(right);
52 parser.parse();
53 if parser.ansi.as_ptr() == right.as_ptr() {
54 writer.write_all(&right[..1])?;
55 ansi = &right[1..];
56 } else {
57 if parser.reset {
58 writer.reset()?;
59 } else {
60 writer.set_color(&parser.spec)?;
61 }
62 ansi = parser.ansi;
63 }
64 }
65 writer.write_all(ansi)
66}
67
68#[derive(Copy, Clone, PartialEq, Eq, Debug)]
69enum State {
70 Normal,
71 PrepareCustomColor,
72 Ansi256,
73 Rgb,
74}
75#[derive(Debug)]
76struct ColorSpecParser<'a> {
77 spec: ColorSpec,
78 ansi: &'a [u8],
79 reset: bool,
80 state: State,
81 is_bg: bool,
82 red: Option<u8>,
83 green: Option<u8>,
84}
85impl<'a> ColorSpecParser<'a> {
86 fn new(ansi: &'a [u8]) -> Self {
87 Self {
88 spec: ColorSpec::new(),
89 ansi,
90 reset: false,
91 state: State::Normal,
92 is_bg: false,
93 red: None,
94 green: None,
95 }
96 }
97
98 fn parse(&mut self) {
99 #[derive(PartialEq, Eq, Debug)]
100 enum Expected {
101 Escape,
102 OpenBracket,
103 Number(u8),
104 }
105
106 while !self.ansi.is_empty() {
107 let mut expected = Expected::Escape;
108 let mut it = self.ansi.iter();
109 for b in &mut it {
110 match (*b, expected) {
111 (0x1b, Expected::Escape) => {
112 expected = Expected::OpenBracket;
113 continue;
114 }
115 (b'[', Expected::OpenBracket) => {
116 expected = Expected::Number(0);
117 continue;
118 }
119 (b'0'..=b'9', Expected::Number(number)) => {
120 if let Some(n) = number.checked_mul(10).and_then(|n| n.checked_add(b - b'0')) {
121 expected = Expected::Number(n);
122 continue;
123 }
124 }
125 (b':', Expected::Number(number))
126 | (b';', Expected::Number(number))
127 | (b'm', Expected::Number(number)) => {
128 self.apply_number(number);
129 if *b == b'm' {
130 expected = Expected::Escape;
131 break;
132 } else {
133 expected = Expected::Number(0);
134 continue;
135 }
136 }
137 _ => {}
138 }
139 return;
140 }
141 if let Expected::Escape = expected {
142 self.ansi = it.as_slice();
143 } else {
144 break;
145 }
146 }
147 }
148
149 fn set_color(&mut self, color: Color) {
150 if self.is_bg {
151 self.spec.set_bg(Some(color));
152 } else {
153 self.spec.set_fg(Some(color));
154 }
155 }
156
157 fn apply_number(&mut self, number: u8) {
158 self.reset = false;
159 match (number, self.state) {
160 (0, State::Normal) => {
161 self.reset = true;
162 }
163 (1, State::Normal) => {
164 self.spec.set_bold(true);
165 }
166 (4, State::Normal) => {
167 self.spec.set_underline(true);
168 }
169 (21, State::Normal) => {
170 self.spec.set_bold(false);
171 }
172 (24, State::Normal) => {
173 self.spec.set_underline(false);
174 }
175 (38, State::Normal) | (48, State::Normal) => {
176 self.is_bg = number == 48;
177 self.state = State::PrepareCustomColor;
178 }
179 (30..=39, State::Normal) => {
180 self.spec.set_fg(parse_color(number - 30));
181 }
182 (40..=49, State::Normal) => {
183 self.spec.set_bg(parse_color(number - 40));
184 }
185 (90..=97, State::Normal) => {
186 self.spec.set_intense(true).set_fg(parse_color(number - 90));
187 }
188 (100..=107, State::Normal) => {
189 self.spec.set_intense(true).set_bg(parse_color(number - 100));
190 }
191 (5, State::PrepareCustomColor) => {
192 self.state = State::Ansi256;
193 }
194 (2, State::PrepareCustomColor) => {
195 self.state = State::Rgb;
196 self.red = None;
197 self.green = None;
198 }
199 (n, State::Ansi256) => {
200 self.set_color(Color::Ansi256(n));
201 self.state = State::Normal;
202 }
203 (b, State::Rgb) => match (self.red, self.green) {
204 (None, _) => {
205 self.red = Some(b);
206 }
207 (Some(_), None) => {
208 self.green = Some(b);
209 }
210 (Some(r), Some(g)) => {
211 self.set_color(Color::Rgb(r, g, b));
212 self.state = State::Normal;
213 }
214 },
215 _ => {
216 self.state = State::Normal;
217 }
218 }
219 }
220}
221
222fn parse_color(digit: u8) -> Option<Color> {
223 match digit {
224 0 => Some(Color::Black),
225 1 => Some(Color::Red),
226 2 => Some(Color::Green),
227 3 => Some(Color::Yellow),
228 4 => Some(Color::Blue),
229 5 => Some(Color::Magenta),
230 6 => Some(Color::Cyan),
231 7 => Some(Color::White),
232 _ => None,
233 }
234}