ansiconst/ansi/
effect.rs

1use crate::{Effect, Toggle};
2use crate::write::{compile_time, run_time};
3use crate::introspect::Attr;
4use bitflags::bitflags;
5use std::fmt;
6
7bitflags! {
8    #[derive(PartialEq, Eq, Clone, Copy, fmt::Debug)]
9    pub(super) struct Attrs: u8 {
10        const Bold      = 1 << 0;
11        const Faint     = 1 << 1;
12        const Italic    = 1 << 2;
13        const Underline = 1 << 3;
14        const Blink     = 1 << 4;
15        const Reverse   = 1 << 5;
16        const Hidden    = 1 << 6;
17        const Strike    = 1 << 7;
18    }
19}
20
21impl Attrs {
22    #[inline]
23    pub(super) const fn contains_effect(&self, effect: Effect) -> bool {
24        self.contains(Self::from_effect(effect))
25    }
26
27    #[inline]
28    const fn from_effect(effect: Effect) -> Self {
29        match effect {
30            Effect::Bold      => Self::Bold,
31            Effect::Faint     => Self::Faint,
32            Effect::Italic    => Self::Italic,
33            Effect::Underline => Self::Underline,
34            Effect::Blink     => Self::Blink,
35            Effect::Reverse   => Self::Reverse,
36            Effect::Hidden    => Self::Hidden,
37            Effect::Strike    => Self::Strike,
38        }
39    }
40
41    /// Includes other attributes that are also reset when self's `reset` ANSI codes are applied.
42    #[inline]
43    pub(super) const fn with_overlaps(&self) -> Self {
44        if self.intersects(Attrs::Bold) {
45            self.union(Attrs::Faint)
46        } else if self.intersects(Attrs::Faint) {
47            self.union(Attrs::Bold)
48        } else {
49            *self
50        }
51    }
52
53    /// Excludes other attributes that are also reset when self's `reset` ANSI codes are applied.
54    #[inline]
55    pub(super) const fn no_overlaps(&self) -> Self {
56        if self.contains(Attrs::Bold.union(Attrs::Faint)) {
57            self.difference(Attrs::Faint)
58        } else {
59            *self
60        }
61    }
62}
63
64#[derive(PartialEq, Eq, Clone, Copy)]
65pub(super) struct Effects { y: Attrs, n: Attrs }
66
67impl Effects {
68    #[inline]
69    pub(super) const fn from_effect(effect: Effect, toggle: Toggle) -> Self {
70        Self::new(Attrs::from_effect(effect), toggle)
71    }
72
73    #[inline]
74    const fn new(attrs: Attrs, toggle: Toggle) -> Self {
75        match toggle {
76            Toggle::Set   => Self { y: attrs,          n: Attrs::empty() },
77            Toggle::Reset => Self { y: Attrs::empty(), n: attrs          },
78        }
79    }
80    #[inline]
81    pub(super) const fn is_empty(&self) -> bool { self.y.is_empty() && self.n.is_empty() }
82    #[inline]
83    pub(super) const fn is_reset(&self) -> bool { self.y.is_empty() && self.n.is_all() }
84    #[inline]
85    pub(super) const fn empty() -> Self{ Effects { y: Attrs::empty(), n: Attrs::empty() } }
86    #[inline]
87    pub(super) const fn reset() -> Self{ Effects { y: Attrs::empty(), n: Attrs::all() } }
88    #[inline]
89    pub(super) const fn get_effect(&self, ef: Effect) -> Option<Attr<Effect>> {
90        let attrs = Attrs::from_effect(ef);
91        if self.y.contains(attrs) {
92            Some(Attr::new_effect(ef, Toggle::Set))
93        } else if self.n.contains(attrs) {
94            Some(Attr::new_effect(ef, Toggle::Reset))
95        } else {
96            None
97        }
98    }
99    #[inline]
100    pub(super) const fn add(&self, other: Self) -> Self {
101        let other_attrs = other.attrs();
102        Self {
103            y: self.y.difference(other_attrs).union(other.y),
104            n: self.n.difference(other_attrs).union(other.n),
105        }
106    }
107    #[inline]
108    pub(super) const fn transition(&self, to_other: Self) -> Self {
109        // 1. Include other's non-overlapping .n
110        let other_new_n = to_other.n.difference(self.n.with_overlaps());
111        // 2. Include resets for self's non-overlapping .y
112        let self_kill_y = self.y.difference(to_other.y).difference(other_new_n.with_overlaps());
113        // 3. Restore other's .y that were indirectly reset by #1 & #2
114        let other_restore_y = to_other.y.intersection(self_kill_y.with_overlaps().union(other_new_n.with_overlaps()));
115        // 4. Combine other's non-overlapping .y and #1, #2, #3
116        let y = to_other.y.difference(self.y).union(other_restore_y);
117        let n = other_new_n.union(self_kill_y).no_overlaps();
118        #[cfg(test)]
119        assert!(!y.intersects(n));
120        Self { y, n }
121    }
122    #[inline]
123    pub(super) const fn not(&self) -> Self {
124        Self {
125            y: Attrs::empty(),
126            n: self.y,
127        }
128    }
129    #[inline]
130    pub(super) const fn only(&self) -> Self {
131        Self {
132            y: self.y,
133            n: self.n.union(self.y.complement()),
134        }
135    }
136    #[inline]
137    pub(super) const fn remove(&self, attrs: Attrs) -> Self {
138        Self {
139            y: self.y.difference(attrs),
140            n: self.n.difference(attrs),
141        }
142    }
143    #[inline]
144    pub(super) const fn attrs(&self) -> Attrs {
145        self.y.union(self.n)
146    }
147
148    #[inline]
149    pub(super) fn write(&self, w: &mut run_time::Formatter<'_,'_>, toggle: Toggle) -> fmt::Result {
150        let attrs = match toggle {
151            Toggle::Set   => self.y,
152            Toggle::Reset => self.n,
153        };
154        if attrs.contains(Attrs::Bold     ) { w.write_effect(Effect::Bold,      toggle)?; }
155        if attrs.contains(Attrs::Faint    ) { w.write_effect(Effect::Faint,     toggle)?; }
156        if attrs.contains(Attrs::Italic   ) { w.write_effect(Effect::Italic,    toggle)?; }
157        if attrs.contains(Attrs::Underline) { w.write_effect(Effect::Underline, toggle)?; }
158        if attrs.contains(Attrs::Blink    ) { w.write_effect(Effect::Blink,     toggle)?; }
159        if attrs.contains(Attrs::Reverse  ) { w.write_effect(Effect::Reverse,   toggle)?; }
160        if attrs.contains(Attrs::Hidden   ) { w.write_effect(Effect::Hidden,    toggle)?; }
161        if attrs.contains(Attrs::Strike   ) { w.write_effect(Effect::Strike,    toggle)?; }
162        Ok(())
163    }
164    #[inline]
165    pub(super) const fn write_const(&self, mut w: compile_time::Writer, toggle: Toggle) -> compile_time::Writer {
166        let attrs = match toggle {
167            Toggle::Set   => self.y,
168            Toggle::Reset => self.n,
169        };
170        if attrs.contains(Attrs::Bold     ) { w = w.write_effect(Effect::Bold,      toggle); }
171        if attrs.contains(Attrs::Faint    ) { w = w.write_effect(Effect::Faint,     toggle); }
172        if attrs.contains(Attrs::Italic   ) { w = w.write_effect(Effect::Italic,    toggle); }
173        if attrs.contains(Attrs::Underline) { w = w.write_effect(Effect::Underline, toggle); }
174        if attrs.contains(Attrs::Blink    ) { w = w.write_effect(Effect::Blink,     toggle); }
175        if attrs.contains(Attrs::Reverse  ) { w = w.write_effect(Effect::Reverse,   toggle); }
176        if attrs.contains(Attrs::Hidden   ) { w = w.write_effect(Effect::Hidden,    toggle); }
177        if attrs.contains(Attrs::Strike   ) { w = w.write_effect(Effect::Strike,    toggle); }
178        w
179    }
180}
181
182impl From<&Effect> for Effects {
183    fn from(ef: &Effect) -> Self { Self::from_effect(*ef, Toggle::Set) }
184}
185impl From<Effect> for Effects {
186    fn from(ef: Effect) -> Self { Self::from_effect(ef, Toggle::Set) }
187}