ziyy_core/parser/ansi/
mod.rs

1use crate::parser::color::Color;
2// pub use effect::{DuoEffect, Effect};
3pub 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)?; // all in ASCII range
277        }
278
279        Ok(())
280    }
281}
282
283impl AddAssign for Ansi {
284    /// Add two Ansi styles together.
285    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    /// Difference between self and rhs.
311    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    /// Invert Ansi style.
339    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}