ziyy_core/parser/ansi/
effect.rs

1use std::ops::{Add, Not, Sub};
2
3/// Ansi effect
4#[derive(Default, Debug, PartialEq)]
5pub enum Effect {
6    #[default]
7    None,
8    Apply,
9    Clear,
10}
11
12impl Effect {
13    pub fn is_set(&self) -> bool {
14        !matches!(self, Effect::None)
15    }
16
17    pub fn is_unset(&self) -> bool {
18        !self.is_set()
19    }
20}
21
22impl Add for Effect {
23    type Output = Effect;
24
25    fn add(self, rhs: Self) -> Self::Output {
26        match (self, rhs) {
27            (Effect::None, Effect::None) => Effect::None,
28            (Effect::None, Effect::Apply) => Effect::Apply,
29            (Effect::None, Effect::Clear) => Effect::None,
30            (Effect::Apply, Effect::None) => Effect::Apply,
31            (Effect::Apply, Effect::Apply) => Effect::Apply,
32            (Effect::Apply, Effect::Clear) => Effect::Clear,
33            (Effect::Clear, Effect::None) => Effect::Clear,
34            (Effect::Clear, Effect::Apply) => Effect::Apply,
35            (Effect::Clear, Effect::Clear) => Effect::Clear,
36        }
37    }
38}
39
40impl Sub for Effect {
41    type Output = Effect;
42
43    fn sub(self, rhs: Self) -> Self::Output {
44        match (self, rhs) {
45            (Effect::None, _) => Effect::None,
46            (Effect::Apply, Effect::Apply) => Effect::None,
47            (Effect::Clear, Effect::Clear) => Effect::None,
48            (Effect::Apply, _) => Effect::Apply,
49            (Effect::Clear, Effect::None) => Effect::None,
50            (Effect::Clear, _) => Effect::Clear,
51        }
52    }
53}
54
55impl Not for Effect {
56    type Output = Effect;
57
58    fn not(self) -> Self::Output {
59        match self {
60            Effect::None => Effect::None,
61            Effect::Apply => Effect::Clear,
62            Effect::Clear => Effect::None,
63        }
64    }
65}
66
67impl From<(bool, bool)> for Effect {
68    fn from(value: (bool, bool)) -> Self {
69        match value {
70            (false, false) => Effect::None,
71            (true, false) => Effect::Apply,
72            (false, true) => Effect::Clear,
73            _ => unreachable!("Invalid Effect"),
74        }
75    }
76}
77
78/// Two Effects that are cleared by same ansi sequence and only
79/// one can be set at a time.
80#[derive(Debug, Clone, Copy, PartialEq, Default)]
81pub enum DuoEffect {
82    #[default]
83    /// No effect is set
84    None,
85    /// The first effect is set
86    A,
87    /// The second effect is set
88    B,
89    /// The effect changed from the first effect to the second effect
90    AB,
91    /// The effect changed from the second effect to the first effect
92    BA,
93    AE,
94    BE,
95    /// Both effects are cleared
96    E,
97}
98
99impl DuoEffect {
100    /// If any effect is set
101    pub fn is_set(&self) -> bool {
102        !matches!(self, DuoEffect::None)
103    }
104
105    /// If no effect is set
106    pub fn is_unset(&self) -> bool {
107        !self.is_set()
108    }
109}
110
111impl Add for DuoEffect {
112    type Output = DuoEffect;
113
114    /// Merges two [DuoEffect]. This method is not commutative.
115    /// `self` is the initial [DuoEffect] while `rhs` is a new state it is moving to.
116    /// The result is the value after such movement.
117    fn add(self, rhs: Self) -> Self::Output {
118        match (self, rhs) {
119            // do no clear, since nothing is set
120            (DuoEffect::None, DuoEffect::E | DuoEffect::AE | DuoEffect::BE) => DuoEffect::None,
121            // we have left the initial state
122            (DuoEffect::None, rhs) => rhs,
123            // we can't go back to initial state without an explicit clear
124            (lhs, DuoEffect::None) => lhs,
125            (DuoEffect::E, rhs) => rhs,
126            // downgrade clear all to only clear A and resist upgrade
127            (DuoEffect::A | DuoEffect::BA | DuoEffect::AE, DuoEffect::E) => DuoEffect::AE,
128            // downgrade clear all to only clear B and resist upgrade
129            (DuoEffect::B | DuoEffect::AB | DuoEffect::BE, DuoEffect::E) => DuoEffect::BE,
130            // similar effects so keep it
131            (DuoEffect::A | DuoEffect::BA, DuoEffect::A) => DuoEffect::A,
132            // similar effects so keep it
133            (DuoEffect::B | DuoEffect::AB, DuoEffect::B) => DuoEffect::B,
134            // track movement of effect
135            (DuoEffect::A | DuoEffect::BA, DuoEffect::B) => DuoEffect::AB,
136            // track movement of effect
137            (DuoEffect::B | DuoEffect::AB, DuoEffect::A) => DuoEffect::BA,
138            // clear A and BA only
139            (DuoEffect::A | DuoEffect::BA, DuoEffect::AE) => DuoEffect::AE,
140            // clear B and AB only
141            (DuoEffect::B | DuoEffect::AB, DuoEffect::BE) => DuoEffect::BE,
142
143            (DuoEffect::AE, DuoEffect::B) => DuoEffect::AB,
144            (DuoEffect::BE, DuoEffect::A) => DuoEffect::BA,
145            // AE can only clear A and BA
146            (lhs, DuoEffect::AE) => lhs,
147            // BE can only clear B and AB
148            (lhs, DuoEffect::BE) => lhs,
149            // all others, keep `rhs`
150            (_, rhs) => rhs,
151        }
152    }
153}
154
155impl Sub for DuoEffect {
156    type Output = DuoEffect;
157
158    /// Differentiates two [DuoEffect]. This is used by [Resolver::optimize_styles][crate::Resolver::optimize_styles]
159    /// to ensure an effect is declared once and cleared once.
160    fn sub(self, rhs: Self) -> Self::Output {
161        match (self, rhs) {
162            (DuoEffect::E, DuoEffect::None) => DuoEffect::None,
163            // if none, no need to redeclare effect or clear all effects
164            (DuoEffect::None | DuoEffect::E, _) => self,
165            // we have left the initial state or state of being cleared
166            (lhs, DuoEffect::None | DuoEffect::E) => lhs,
167            // we can't declare an effect twice
168            (DuoEffect::A, DuoEffect::A | DuoEffect::BA) => DuoEffect::None,
169            //
170            (DuoEffect::B, DuoEffect::A | DuoEffect::BA) => DuoEffect::AB,
171            (DuoEffect::A, DuoEffect::B | DuoEffect::AB) => DuoEffect::BA,
172            (DuoEffect::B, DuoEffect::B | DuoEffect::AB) => DuoEffect::None,
173            (DuoEffect::AE, DuoEffect::A | DuoEffect::BA) => DuoEffect::AE,
174            (DuoEffect::BE, DuoEffect::B | DuoEffect::AB) => DuoEffect::BE,
175            (DuoEffect::AE, _) => DuoEffect::None,
176            (DuoEffect::BE, _) => DuoEffect::None,
177            (lhs, _) => lhs,
178        }
179    }
180}
181
182impl Not for DuoEffect {
183    type Output = DuoEffect;
184
185    fn not(self) -> Self::Output {
186        match self {
187            DuoEffect::None => DuoEffect::None,
188            DuoEffect::A | DuoEffect::BA => DuoEffect::AE,
189            DuoEffect::B | DuoEffect::AB => DuoEffect::BE,
190            DuoEffect::E | DuoEffect::AE | DuoEffect::BE => DuoEffect::None,
191        }
192    }
193}
194
195impl From<(bool, bool, bool)> for DuoEffect {
196    fn from(value: (bool, bool, bool)) -> Self {
197        match value {
198            (false, false, false) => DuoEffect::None,
199            (true, false, false) => DuoEffect::A,
200            (false, true, false) => DuoEffect::B,
201            (true, true, false) => DuoEffect::AB,
202            (false, false, true) => DuoEffect::BA,
203            (false, true, true) => DuoEffect::AE,
204            (true, false, true) => DuoEffect::BE,
205            (true, true, true) => DuoEffect::E,
206        }
207    }
208}