fluent_ansi/style/
encoded_effects.rs

1use crate::{AllEffects, Effect, UnderlineEffect};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
4pub(crate) struct EncodedEffects(u16);
5
6impl EncodedEffects {
7    #[must_use]
8    pub(crate) const fn new() -> Self {
9        Self(0)
10    }
11
12    #[must_use]
13    pub(crate) fn set(self, effect: Effect, value: bool) -> Self {
14        if value {
15            self.add(effect)
16        } else {
17            self.remove(effect)
18        }
19    }
20
21    #[must_use]
22    fn add(self, effect: Effect) -> Self {
23        let underline_effect = UnderlineEffect::all().find(|t| t.to_effect() == effect);
24        if let Some(underline_effect) = underline_effect {
25            self.set_underline(Some(underline_effect))
26        } else {
27            self.set_bit(effect)
28        }
29    }
30
31    #[must_use]
32    fn remove(self, effect: Effect) -> Self {
33        self.clear_bit(effect)
34    }
35
36    #[must_use]
37    pub(crate) fn set_underline(self, underline_effect: Option<UnderlineEffect>) -> Self {
38        let encoded_effects = self.remove_underline();
39        if let Some(underline_effect) = underline_effect {
40            encoded_effects.set_bit(underline_effect.to_effect())
41        } else {
42            encoded_effects
43        }
44    }
45
46    #[must_use]
47    fn remove_underline(self) -> Self {
48        let mut encoded_effects = self;
49        for underline_effect in UnderlineEffect::all() {
50            encoded_effects = encoded_effects.clear_bit(underline_effect.to_effect());
51        }
52        encoded_effects
53    }
54
55    #[must_use]
56    pub(crate) fn get(self, effect: Effect) -> bool {
57        self.0 & Self::bit_mask(effect) != 0
58    }
59
60    #[must_use]
61    pub(crate) fn get_effects(self) -> GetEffects {
62        GetEffects {
63            inner: Effect::all(),
64            encoded_effects: self,
65        }
66    }
67
68    #[must_use]
69    fn set_bit(self, effect: Effect) -> Self {
70        let bits = self.0 | Self::bit_mask(effect);
71        Self(bits)
72    }
73
74    #[must_use]
75    fn clear_bit(self, effect: Effect) -> Self {
76        let bits = self.0 & !Self::bit_mask(effect);
77        Self(bits)
78    }
79
80    #[must_use]
81    fn bit_mask(effect: Effect) -> u16 {
82        let bit_index = effect as u16;
83        1 << bit_index
84    }
85}
86
87/// An iterator over the effects that are currently set.
88#[derive(Debug, Clone)]
89pub struct GetEffects {
90    inner: AllEffects,
91    encoded_effects: EncodedEffects,
92}
93impl Iterator for GetEffects {
94    type Item = Effect;
95
96    fn next(&mut self) -> Option<Self::Item> {
97        self.inner
98            .by_ref()
99            .find(|&effect| self.encoded_effects.get(effect))
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    fn underline_effects() -> impl Iterator<Item = Effect> {
108        UnderlineEffect::all().map(UnderlineEffect::to_effect)
109    }
110
111    #[test]
112    fn default() {
113        let effects = EncodedEffects::default();
114
115        for effect in Effect::all() {
116            assert_eq!(effects.get(effect), false);
117        }
118    }
119
120    #[test]
121    fn set_true() {
122        for added_effect in Effect::all() {
123            let effects = EncodedEffects(0).set(added_effect, true);
124
125            for checked_effect in Effect::all() {
126                assert_eq!(
127                    effects.get(checked_effect),
128                    added_effect == checked_effect,
129                    "{added_effect:?} {checked_effect:?}"
130                );
131            }
132        }
133    }
134
135    #[test]
136    fn set_false() {
137        for removed_effect in Effect::all() {
138            let effects = EncodedEffects(!0).set(removed_effect, false);
139
140            for checked_effect in Effect::all() {
141                assert_eq!(
142                    effects.get(checked_effect),
143                    removed_effect != checked_effect,
144                    "{removed_effect:?} {checked_effect:?}"
145                );
146            }
147        }
148    }
149
150    #[test]
151    fn set_underline_effect_true() {
152        for initial_effect in underline_effects() {
153            // Add some effect
154            let encoded_effects = EncodedEffects::default().set(initial_effect, true);
155
156            for other_effect in underline_effects() {
157                if other_effect == initial_effect {
158                    continue;
159                }
160
161                // Add other effect
162                let encoded_effects = encoded_effects.set(other_effect, true);
163
164                // Only the most recently added effect should be set
165                assert!(
166                    !encoded_effects.get(initial_effect),
167                    "{initial_effect:?} should not be set"
168                );
169                assert!(
170                    encoded_effects.get(other_effect),
171                    "{other_effect:?} should be set"
172                );
173            }
174        }
175    }
176
177    #[test]
178    fn set_underline_some() {
179        for initial_effect in UnderlineEffect::all() {
180            // Add some effect
181            let encoded_effects = EncodedEffects::default().set(initial_effect.to_effect(), true);
182
183            for other_effect in UnderlineEffect::all() {
184                if other_effect == initial_effect {
185                    continue;
186                }
187
188                // Add other effect
189                let encoded_effects = encoded_effects.set_underline(Some(other_effect));
190
191                // Only the most recently added effect should be set
192                assert!(
193                    !encoded_effects.get(initial_effect.to_effect()),
194                    "{initial_effect:?} should not be set"
195                );
196                assert!(
197                    encoded_effects.get(other_effect.to_effect()),
198                    "{other_effect:?} should be set"
199                );
200            }
201        }
202    }
203
204    macro_rules! test_clear_underline {
205        ($method:ident ( $( $arg:expr )?) ) => {
206            for initial_effect in UnderlineEffect::all() {
207                // Add some effect
208                let encoded_effects = EncodedEffects::default().set(initial_effect.to_effect(), true);
209
210                // Clear underline effect
211                let encoded_effects = encoded_effects.$method( $( $arg )? );
212
213                for checked_effect in UnderlineEffect::all() {
214                    assert!(
215                        !encoded_effects.get(checked_effect.to_effect()),
216                        "{initial_effect:?} should not be set"
217                    );
218                }
219            }
220        };
221    }
222
223    #[test]
224    fn set_underline_none() {
225        test_clear_underline!(set_underline(None));
226    }
227
228    #[test]
229    fn remove_underline() {
230        test_clear_underline!(remove_underline());
231    }
232
233    #[test]
234    fn get_effects() {
235        let effects = EncodedEffects::default()
236            .add(Effect::Bold)
237            .add(Effect::SolidUnderline);
238
239        let mut get_effects = effects.get_effects();
240
241        assert_eq!(get_effects.next(), Some(Effect::Bold));
242        assert_eq!(get_effects.next(), Some(Effect::SolidUnderline));
243        assert_eq!(get_effects.next(), None);
244    }
245}