1use crate::parser::color::Color;
2pub use options::AnsiOptions;
4use std::fmt::{Debug, Display, Write};
5use std::io::Write as _;
6use std::ops::{Add, AddAssign, Not, Sub, SubAssign};
7
8pub use effect::{DuoEffect, Effect};
9
10mod effect;
11mod options;
12
13#[derive(Clone, PartialEq, Eq, Hash)]
14pub struct Ansi {
15 pub(crate) style: u32,
16 colors: [Color; 2],
17}
18
19impl Default for Ansi {
20 fn default() -> Self {
21 Self::new()
22 }
23}
24
25impl Ansi {
26 pub fn new() -> Self {
27 Ansi {
28 style: 0,
29 colors: [const { Color::new() }; 2],
30 }
31 }
32
33 pub fn with(options: AnsiOptions) -> Self {
34 let mut ansi = Ansi::new();
35 ansi.set_brightness(options.brightness);
36 ansi.set_under(options.under);
37 ansi.set_blink(options.blink);
38 ansi.set_hidden(options.hidden);
39 ansi.set_italics(options.italics);
40 ansi.set_negative(options.negetive);
41 ansi.set_strike(options.strike);
42 ansi.set_fg_color(options.fg_color);
43 ansi.set_bg_color(options.bg_color);
44
45 ansi
46 }
47
48 pub fn clear_all(&mut self) {
49 self.set_brightness(DuoEffect::E);
50 self.set_under(DuoEffect::E);
51 self.set_blink(Effect::Clear);
52 self.set_hidden(Effect::Clear);
53 self.set_italics(Effect::Clear);
54 self.set_negative(Effect::Clear);
55 self.set_strike(Effect::Clear);
56 self.set_fg_color(Color::four_bit(39));
57 self.set_bg_color(Color::four_bit(49));
58 }
59}
60
61fn get_style(style: &u32, offset: u32) -> bool {
62 let n = (style >> (Ansi::L - offset)) & 1;
63 n == 1
64}
65
66fn set_style(style: &mut u32, offset: u32, value: bool) {
67 if value {
68 *style |= 1 << (Ansi::L - offset);
69 } else if get_style(style, offset) {
70 *style ^= 1 << (Ansi::L - offset);
71 }
72}
73
74macro_rules! impl_ansi {
75 (
76 $( ( $i:expr, $set_x:tt, $x:tt ) ),*;
77 $( ( $j:expr, $set_y:tt, $y:tt ) ),*;
78 $( ( $k:expr, $set_z:tt, $z:tt ) ),*
79 ) => {
80 impl Ansi {
81 const L: u32 = 31;
82
83 $(
84 pub fn $set_x(&mut self, value: DuoEffect) {
85 match value {
86 DuoEffect::None => {
87 set_style(&mut self.style, $i, false);
88 set_style(&mut self.style, $i + 1, false);
89 set_style(&mut self.style, $i + 2, false);
90 }
91 DuoEffect::A => {
92 set_style(&mut self.style, $i, true);
93 set_style(&mut self.style, $i + 1, false);
94 set_style(&mut self.style, $i + 2, false);
95 }
96 DuoEffect::B => {
97 set_style(&mut self.style, $i, false);
98 set_style(&mut self.style, $i + 1, true);
99 set_style(&mut self.style, $i + 2, false);
100 }
101 DuoEffect::AB => {
102 set_style(&mut self.style, $i, true);
103 set_style(&mut self.style, $i + 1, true);
104 set_style(&mut self.style, $i + 2, false);
105 }
106 DuoEffect::BA => {
107 set_style(&mut self.style, $i, false);
108 set_style(&mut self.style, $i + 1, false);
109 set_style(&mut self.style, $i + 2, true);
110 }
111 DuoEffect::AE => {
112 set_style(&mut self.style, $i, false);
113 set_style(&mut self.style, $i + 1, true);
114 set_style(&mut self.style, $i + 2, true);
115 }
116 DuoEffect::BE => {
117 set_style(&mut self.style, $i, true);
118 set_style(&mut self.style, $i + 1, false);
119 set_style(&mut self.style, $i + 2, true);
120 }
121 DuoEffect::E => {
122 set_style(&mut self.style, $i, true);
123 set_style(&mut self.style, $i + 1, true);
124 set_style(&mut self.style, $i + 2, true);
125 }
126 }
127 }
128
129 pub fn $x(&self) -> DuoEffect {
130 (
131 get_style(&self.style, $i),
132 get_style(&self.style, $i + 1),
133 get_style(&self.style, $i + 2)
134 ).into()
135 }
136 )*
137
138 $(
139 pub fn $set_y(&mut self, value: Effect) {
140 match value {
141 Effect::None => {
142 set_style(&mut self.style, $j, false);
143 set_style(&mut self.style, $j + 1, false);
144 }
145 Effect::Apply => {
146 set_style(&mut self.style, $j, true);
147 set_style(&mut self.style, $j + 1, false);
148 },
149 Effect::Clear => {
150 set_style(&mut self.style, $j, false);
151 set_style(&mut self.style, $j + 1, true);
152 }
153 }
154
155 }
156
157 pub fn $y(&self) -> Effect {
158 (get_style(&self.style, $j), get_style(&self.style, $j + 1)).into()
159 }
160 )*
161
162 $(
163 pub fn $set_z(&mut self, value: Color) {
164 self.colors[$k] = value;
165 }
166
167 pub fn $z(&self) -> &Color {
168 &self.colors[$k]
169 }
170 )*
171 }
172 };
173}
174
175impl_ansi![
176 (0, set_brightness, brightness),
177 (3, set_under, under);
178
179 (6, set_blink, blink),
180 (8, set_hidden, hidden),
181 (10, set_italics, italics),
182 (12, set_negative, negative),
183 (14, set_strike, strike);
184
185 (0, set_fg_color, fg_color),
186 (1, set_bg_color, bg_color)
187];
188
189impl Debug for Ansi {
190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191 f.debug_struct("Ansi")
192 .field("brightness", &self.brightness())
193 .field("under", &self.under())
194 .field("blink", &self.blink())
195 .field("hidden", &self.hidden())
196 .field("italics", &self.italics())
197 .field("negative", &self.negative())
198 .field("strike", &self.strike())
199 .field("fg_color", self.fg_color())
200 .field("bg_color", self.bg_color())
201 .finish()
202 }
203}
204
205impl Display for Ansi {
206 #[allow(clippy::recursive_format_impl)]
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 if f.alternate() {
209 return f.write_fmt(format_args!("\"{}\"", self.to_string().escape_debug()));
210 }
211
212 let mut buf = Vec::with_capacity(128);
213 let _ = buf.write(b"\x1b[");
214 macro_rules! write_prop_style {
215 ( $f:tt, $a:expr, $e:expr ) => {
216 match self.$f() {
217 Effect::None => {}
218 Effect::Apply => {
219 let _ = buf.write($a);
220 }
221 Effect::Clear => {
222 let _ = buf.write($e);
223 }
224 }
225 };
226 }
227
228 macro_rules! write_prop_state {
229 ( $f:tt, $a:expr, $b:expr, $e:expr ) => {
230 match self.$f() {
231 DuoEffect::None => {}
232 DuoEffect::A => {
233 let _ = buf.write($a);
234 }
235 DuoEffect::B => {
236 let _ = buf.write($b);
237 }
238 DuoEffect::AB => {
239 let _ = buf.write($e);
240 let _ = buf.write($b);
241 }
242 DuoEffect::BA => {
243 let _ = buf.write($e);
244 let _ = buf.write($a);
245 }
246 DuoEffect::E | DuoEffect::AE | DuoEffect::BE => {
247 let _ = buf.write($e);
248 }
249 }
250 };
251 }
252
253 write_prop_state!(brightness, b"1;", b"2;", b"22;");
254 write_prop_state!(under, b"4;", b"21;", b"24;");
255
256 write_prop_style!(italics, b"3;", b"23;");
257 write_prop_style!(blink, b"5;", b"25;");
258 write_prop_style!(negative, b"7;", b"27;");
259 write_prop_style!(hidden, b"8;", b"28;");
260 write_prop_style!(strike, b"9;", b"29;");
261
262 let _ = buf.write(self.fg_color().to_string().as_bytes());
263 let _ = buf.write(self.bg_color().to_string().as_bytes());
264
265 if buf[buf.len() - 1] == b';' {
266 buf.pop();
267 }
268
269 buf.push(b'm');
270
271 if buf.len() == 3 {
272 buf.clear();
273 }
274
275 for ch in buf {
276 f.write_char(ch as char)?; }
278
279 Ok(())
280 }
281}
282
283impl AddAssign for Ansi {
284 fn add_assign(&mut self, rhs: Self) {
286 self.set_brightness(self.brightness() + rhs.brightness());
287 self.set_under(self.under() + rhs.under());
288
289 self.set_blink(self.blink() + rhs.blink());
290 self.set_hidden(self.hidden() + rhs.hidden());
291 self.set_italics(self.italics() + rhs.italics());
292 self.set_negative(self.negative() + rhs.negative());
293 self.set_strike(self.strike() + rhs.strike());
294
295 self.colors[0] += rhs.colors[0].clone();
296 self.colors[1] += rhs.colors[1].clone();
297 }
298}
299
300impl Add for Ansi {
301 type Output = Self;
302
303 fn add(mut self, rhs: Self) -> Self::Output {
304 self += rhs;
305 self
306 }
307}
308
309impl SubAssign for Ansi {
310 fn sub_assign(&mut self, rhs: Self) {
312 self.set_brightness(self.brightness() - rhs.brightness());
313 self.set_under(self.under() - rhs.under());
314
315 self.set_blink(self.blink() - rhs.blink());
316 self.set_hidden(self.hidden() - rhs.hidden());
317 self.set_italics(self.italics() - rhs.italics());
318 self.set_negative(self.negative() - rhs.negative());
319 self.set_strike(self.strike() - rhs.strike());
320
321 self.set_fg_color(self.fg_color() - rhs.fg_color());
322 self.set_bg_color(self.bg_color() - rhs.bg_color());
323 }
324}
325
326impl Sub for Ansi {
327 type Output = Self;
328
329 fn sub(mut self, rhs: Self) -> Self::Output {
330 self -= rhs;
331 self
332 }
333}
334
335impl Not for Ansi {
336 type Output = Ansi;
337
338 fn not(mut self) -> Self::Output {
340 self.set_brightness(!self.brightness());
341 self.set_under(!self.under());
342
343 self.set_blink(!self.blink());
344 self.set_hidden(!self.hidden());
345 self.set_italics(!self.italics());
346 self.set_negative(!self.negative());
347 self.set_strike(!self.strike());
348
349 if !self.fg_color().is_empty() {
350 self.set_fg_color(Color::four_bit(39));
351 }
352
353 if !self.bg_color().is_empty() {
354 self.set_bg_color(Color::four_bit(49));
355 }
356
357 self
358 }
359}
360
361#[cfg(test)]
362mod test {
363 use super::*;
364
365 #[test]
366 fn test_ansi_add() {
367 let mut lhs = Ansi::new();
368 lhs.set_brightness(DuoEffect::A);
369
370 let mut rhs = Ansi::new();
371 rhs.set_blink(Effect::Apply);
372 rhs.set_negative(Effect::Apply);
373
374 lhs += rhs;
375
376 assert_eq!(lhs.brightness(), DuoEffect::A);
377 assert_eq!(lhs.under(), DuoEffect::None);
378 assert_eq!(lhs.blink(), Effect::Apply);
379 assert_eq!(lhs.hidden(), Effect::None);
380 assert_eq!(lhs.strike(), Effect::None);
381 assert_eq!(lhs.italics(), Effect::None);
382 assert_eq!(lhs.negative(), Effect::Apply);
383 }
384
385 #[test]
386 fn test_ansi_sub() {
387 let mut lhs = Ansi::new();
388 lhs.set_brightness(DuoEffect::B);
389 lhs.set_blink(Effect::Apply);
390 lhs.set_negative(Effect::Apply);
391
392 let mut rhs = Ansi::new();
393 rhs.set_blink(Effect::Apply);
394 rhs.set_negative(Effect::Apply);
395
396 lhs -= rhs.clone();
397
398 assert_ne!(lhs, rhs);
399
400 assert_eq!(lhs.brightness(), DuoEffect::B);
401 assert_eq!(lhs.under(), DuoEffect::None);
402 assert_eq!(lhs.blink(), Effect::None);
403 assert_eq!(lhs.hidden(), Effect::None);
404 assert_eq!(lhs.strike(), Effect::None);
405 assert_eq!(lhs.italics(), Effect::None);
406 assert_eq!(lhs.negative(), Effect::None);
407 }
408
409 #[test]
410 fn test_ansi_not() {
411 let mut ansi = Ansi::new();
412 ansi.set_brightness(DuoEffect::A);
413 ansi.set_blink(Effect::Apply);
414 ansi.set_negative(Effect::Apply);
415
416 let not_ansi = !ansi.clone();
417
418 assert_eq!(ansi.brightness(), DuoEffect::A);
419 assert_eq!(ansi.under(), DuoEffect::None);
420 assert_eq!(not_ansi.blink(), Effect::Clear);
421 assert_eq!(not_ansi.hidden(), Effect::None);
422 assert_eq!(not_ansi.strike(), Effect::None);
423 assert_eq!(not_ansi.italics(), Effect::None);
424 assert_eq!(not_ansi.negative(), Effect::Clear);
425 }
426}