1#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct Effects(u16);
10
11impl Effects {
12    const PLAIN: Self = Effects(0);
14
15    #[allow(missing_docs)]
16    pub const BOLD: Self = Effects(1 << 0);
17    #[allow(missing_docs)]
18    pub const DIMMED: Self = Effects(1 << 1);
19    pub const ITALIC: Self = Effects(1 << 2);
21    pub const UNDERLINE: Self = Effects(1 << 3);
23    #[allow(missing_docs)]
24    pub const DOUBLE_UNDERLINE: Self = Effects(1 << 4);
25    #[allow(missing_docs)]
26    pub const CURLY_UNDERLINE: Self = Effects(1 << 5);
27    #[allow(missing_docs)]
28    pub const DOTTED_UNDERLINE: Self = Effects(1 << 6);
29    #[allow(missing_docs)]
30    pub const DASHED_UNDERLINE: Self = Effects(1 << 7);
31    #[allow(missing_docs)]
32    pub const BLINK: Self = Effects(1 << 8);
33    pub const INVERT: Self = Effects(1 << 9);
35    #[allow(missing_docs)]
36    pub const HIDDEN: Self = Effects(1 << 10);
37    pub const STRIKETHROUGH: Self = Effects(1 << 11);
39
40    #[inline]
48    pub const fn new() -> Self {
49        Self::PLAIN
50    }
51
52    #[inline]
64    pub const fn is_plain(self) -> bool {
65        self.0 == Self::PLAIN.0
66    }
67
68    #[inline(always)]
80    pub const fn contains(self, other: Effects) -> bool {
81        (other.0 & self.0) == other.0
82    }
83
84    #[inline(always)]
96    #[must_use]
97    pub const fn insert(mut self, other: Effects) -> Self {
98        self.0 |= other.0;
99        self
100    }
101
102    #[inline(always)]
112    #[must_use]
113    pub const fn remove(mut self, other: Effects) -> Self {
114        self.0 &= !other.0;
115        self
116    }
117
118    #[inline(always)]
125    #[must_use]
126    pub const fn clear(self) -> Self {
127        Self::new()
128    }
129
130    #[inline]
139    #[must_use]
140    pub const fn set(self, other: Self, enable: bool) -> Self {
141        if enable {
142            self.insert(other)
143        } else {
144            self.remove(other)
145        }
146    }
147
148    #[inline(always)]
150    pub fn iter(self) -> EffectIter {
151        EffectIter {
152            index: 0,
153            effects: self,
154        }
155    }
156
157    #[inline(always)]
159    pub(crate) fn index_iter(self) -> EffectIndexIter {
160        EffectIndexIter {
161            index: 0,
162            effects: self,
163        }
164    }
165
166    #[inline]
168    pub fn render(self) -> impl core::fmt::Display + Copy {
169        EffectsDisplay(self)
170    }
171
172    #[inline]
173    #[cfg(feature = "std")]
174    pub(crate) fn write_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
175        for index in self.index_iter() {
176            write.write_all(METADATA[index].escape.as_bytes())?;
177        }
178        Ok(())
179    }
180}
181
182impl core::fmt::Debug for Effects {
192    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
193        write!(f, "Effects(")?;
194        for (i, index) in self.index_iter().enumerate() {
195            if i != 0 {
196                write!(f, " | ")?;
197            }
198            write!(f, "{}", METADATA[index].name)?;
199        }
200        write!(f, ")")?;
201        Ok(())
202    }
203}
204
205impl core::ops::BitOr for Effects {
212    type Output = Self;
213
214    #[inline(always)]
215    fn bitor(self, rhs: Self) -> Self {
216        self.insert(rhs)
217    }
218}
219
220impl core::ops::BitOrAssign for Effects {
228    #[inline]
229    fn bitor_assign(&mut self, other: Self) {
230        *self = self.insert(other);
231    }
232}
233
234impl core::ops::Sub for Effects {
241    type Output = Self;
242
243    #[inline]
244    fn sub(self, other: Self) -> Self {
245        self.remove(other)
246    }
247}
248
249impl core::ops::SubAssign for Effects {
257    #[inline]
258    fn sub_assign(&mut self, other: Self) {
259        *self = self.remove(other);
260    }
261}
262
263pub(crate) struct Metadata {
264    pub(crate) name: &'static str,
265    pub(crate) escape: &'static str,
266}
267
268pub(crate) const METADATA: [Metadata; 12] = [
269    Metadata {
270        name: "BOLD",
271        escape: escape!("1"),
272    },
273    Metadata {
274        name: "DIMMED",
275        escape: escape!("2"),
276    },
277    Metadata {
278        name: "ITALIC",
279        escape: escape!("3"),
280    },
281    Metadata {
282        name: "UNDERLINE",
283        escape: escape!("4"),
284    },
285    Metadata {
286        name: "DOUBLE_UNDERLINE",
287        escape: escape!("21"),
288    },
289    Metadata {
290        name: "CURLY_UNDERLINE",
291        escape: escape!("4:3"),
292    },
293    Metadata {
294        name: "DOTTED_UNDERLINE",
295        escape: escape!("4:4"),
296    },
297    Metadata {
298        name: "DASHED_UNDERLINE",
299        escape: escape!("4:5"),
300    },
301    Metadata {
302        name: "BLINK",
303        escape: escape!("5"),
304    },
305    Metadata {
306        name: "INVERT",
307        escape: escape!("7"),
308    },
309    Metadata {
310        name: "HIDDEN",
311        escape: escape!("8"),
312    },
313    Metadata {
314        name: "STRIKETHROUGH",
315        escape: escape!("9"),
316    },
317];
318
319#[derive(Copy, Clone, Default, Debug)]
320struct EffectsDisplay(Effects);
321
322impl core::fmt::Display for EffectsDisplay {
323    #[inline]
324    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
325        for index in self.0.index_iter() {
326            f.write_str(METADATA[index].escape)?;
327        }
328        Ok(())
329    }
330}
331
332#[derive(Clone, Debug, PartialEq, Eq)]
334pub struct EffectIter {
335    index: usize,
336    effects: Effects,
337}
338
339impl Iterator for EffectIter {
340    type Item = Effects;
341
342    fn next(&mut self) -> Option<Self::Item> {
343        while self.index < METADATA.len() {
344            let index = self.index;
345            self.index += 1;
346
347            let effect = Effects(1 << index);
348            if self.effects.contains(effect) {
349                return Some(effect);
350            }
351        }
352
353        None
354    }
355}
356
357#[derive(Clone, Debug, PartialEq, Eq)]
358pub(crate) struct EffectIndexIter {
359    index: usize,
360    effects: Effects,
361}
362
363impl Iterator for EffectIndexIter {
364    type Item = usize;
365
366    fn next(&mut self) -> Option<Self::Item> {
367        while self.index < METADATA.len() {
368            let index = self.index;
369            self.index += 1;
370
371            let effect = Effects(1 << index);
372            if self.effects.contains(effect) {
373                return Some(index);
374            }
375        }
376
377        None
378    }
379}
380
381#[cfg(test)]
382#[cfg(feature = "std")]
383mod test {
384    use super::*;
385
386    #[test]
387    fn print_size_of() {
388        use core::mem::size_of;
389        dbg!(size_of::<Effects>());
390        dbg!(size_of::<EffectsDisplay>());
391    }
392
393    #[test]
394    fn no_align() {
395        #[track_caller]
396        fn assert_no_align(d: impl core::fmt::Display) {
397            let expected = format!("{d}");
398            let actual = format!("{d:<10}");
399            assert_eq!(expected, actual);
400        }
401
402        assert_no_align(Effects::BOLD.render());
403    }
404}