Skip to main content

parse_style/
attributes.rs

1use std::fmt;
2use thiserror::Error;
3
4/// Individual effects that can be applied to text in a terminal.
5///
6/// `Attribute` values can be combined with bitwise operators to produce
7/// [`AttributeSet`]s.
8///
9/// `Attribute` values can be [parsed][std::str::FromStr] from the
10/// following case-insensitive strings:
11///
12/// - `"bold"` or `"b"` — `Bold`
13/// - `"dim"` or `"d"` — `Dim`
14/// - `"italic"` or `"i"` — `Italic`
15/// - `"underline"` or `"u"` — `Underline`
16/// - `"blink"` — `Blink`
17/// - `"blink2"` — `Blink2`
18/// - `"reverse"` or `"r"` — `Reverse`
19/// - `"conceal"` or `"c"` — `Conceal`
20/// - `"strike"` or `"s"` — `Strike`
21/// - `"underline2"` or `"uu"` — `Underline2`
22/// - `"frame"` — `Frame`
23/// - `"encircle"` — `Encircle`
24/// - `"overline"` — `Overline`
25///
26/// `Attribute` values are [displayed][std::fmt::Display] as lowercase
27/// strings from the above list.  For values with two strings, the longer
28/// one is used, unless the alternate display is selected with `"{:#}"`, in
29/// which case the shorter (if any) is used.
30#[derive(Clone, Copy, Debug, strum::EnumIter, Eq, Hash, Ord, PartialEq, PartialOrd)]
31#[repr(u16)]
32pub enum Attribute {
33    Bold = 1 << 0,
34    Dim = 1 << 1,
35    Italic = 1 << 2,
36    Underline = 1 << 3,
37    Blink = 1 << 4,
38    /// Fast blinking
39    Blink2 = 1 << 5,
40    /// Reverse video
41    Reverse = 1 << 6,
42    /// Concealed/hidden
43    Conceal = 1 << 7,
44    /// Strikethrough
45    Strike = 1 << 8,
46    /// Double-underline
47    Underline2 = 1 << 9,
48    Frame = 1 << 10,
49    Encircle = 1 << 11,
50    Overline = 1 << 12,
51}
52
53impl Attribute {
54    const COUNT: u16 = 13;
55
56    /// Returns an iterator over all [`Attribute`] variants
57    pub fn iter() -> AttributeIter {
58        // To avoid the need for users to import the trait
59        <Attribute as strum::IntoEnumIterator>::iter()
60    }
61
62    /// Return the long name of the attribute
63    ///
64    /// # Example
65    ///
66    /// ```
67    /// use parse_style::Attribute;
68    ///
69    /// assert_eq!(Attribute::Bold.as_str(), "bold");
70    /// ```
71    pub fn as_str(self) -> &'static str {
72        match self {
73            Attribute::Bold => "bold",
74            Attribute::Dim => "dim",
75            Attribute::Italic => "italic",
76            Attribute::Underline => "underline",
77            Attribute::Blink => "blink",
78            Attribute::Blink2 => "blink2",
79            Attribute::Reverse => "reverse",
80            Attribute::Conceal => "conceal",
81            Attribute::Strike => "strike",
82            Attribute::Underline2 => "underline2",
83            Attribute::Frame => "frame",
84            Attribute::Encircle => "encircle",
85            Attribute::Overline => "overline",
86        }
87    }
88
89    /// Return the short name of the attribute, or the long name if it doesn't
90    /// have a short name
91    ///
92    /// # Example
93    ///
94    /// ```
95    /// use parse_style::Attribute;
96    ///
97    /// assert_eq!(Attribute::Bold.as_short_str(), "b");
98    /// assert_eq!(Attribute::Blink.as_short_str(), "blink");
99    /// ```
100    pub fn as_short_str(self) -> &'static str {
101        match self {
102            Attribute::Bold => "b",
103            Attribute::Dim => "d",
104            Attribute::Italic => "i",
105            Attribute::Underline => "u",
106            Attribute::Blink => "blink",
107            Attribute::Blink2 => "blink2",
108            Attribute::Reverse => "r",
109            Attribute::Conceal => "c",
110            Attribute::Strike => "s",
111            Attribute::Underline2 => "uu",
112            Attribute::Frame => "frame",
113            Attribute::Encircle => "encircle",
114            Attribute::Overline => "overline",
115        }
116    }
117}
118
119impl fmt::Display for Attribute {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        if f.alternate() {
122            f.write_str(self.as_short_str())
123        } else {
124            f.write_str(self.as_str())
125        }
126    }
127}
128
129impl std::str::FromStr for Attribute {
130    type Err = ParseAttributeError;
131
132    fn from_str(s: &str) -> Result<Attribute, ParseAttributeError> {
133        match s.to_ascii_lowercase().as_str() {
134            "bold" | "b" => Ok(Attribute::Bold),
135            "dim" | "d" => Ok(Attribute::Dim),
136            "italic" | "i" => Ok(Attribute::Italic),
137            "underline" | "u" => Ok(Attribute::Underline),
138            "blink" => Ok(Attribute::Blink),
139            "blink2" => Ok(Attribute::Blink2),
140            "reverse" | "r" => Ok(Attribute::Reverse),
141            "conceal" | "c" => Ok(Attribute::Conceal),
142            "strike" | "s" => Ok(Attribute::Strike),
143            "underline2" | "uu" => Ok(Attribute::Underline2),
144            "frame" => Ok(Attribute::Frame),
145            "encircle" => Ok(Attribute::Encircle),
146            "overline" => Ok(Attribute::Overline),
147            _ => Err(ParseAttributeError(s.to_owned())),
148        }
149    }
150}
151
152impl<A: Into<AttributeSet>> std::ops::BitAnd<A> for Attribute {
153    type Output = AttributeSet;
154
155    fn bitand(self, rhs: A) -> AttributeSet {
156        AttributeSet((self as u16) & rhs.into().0)
157    }
158}
159
160impl<A: Into<AttributeSet>> std::ops::BitOr<A> for Attribute {
161    type Output = AttributeSet;
162
163    fn bitor(self, rhs: A) -> AttributeSet {
164        AttributeSet((self as u16) | rhs.into().0)
165    }
166}
167
168impl<A: Into<AttributeSet>> std::ops::BitXor<A> for Attribute {
169    type Output = AttributeSet;
170
171    fn bitxor(self, rhs: A) -> AttributeSet {
172        AttributeSet((self as u16) ^ rhs.into().0)
173    }
174}
175
176impl<A: Into<AttributeSet>> std::ops::Sub<A> for Attribute {
177    type Output = AttributeSet;
178
179    fn sub(self, rhs: A) -> AttributeSet {
180        AttributeSet((self as u16) & !rhs.into().0)
181    }
182}
183
184impl std::ops::Not for Attribute {
185    type Output = AttributeSet;
186
187    fn not(self) -> AttributeSet {
188        AttributeSet::ALL - self
189    }
190}
191
192/// A set of [`Attribute`] values.
193///
194/// `AttributeSet` values can be combined with bitwise operators and can be
195/// iterated over.
196#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
197pub struct AttributeSet(u16);
198
199impl AttributeSet {
200    /// A set containing no [`Attribute`]s
201    pub const EMPTY: AttributeSet = AttributeSet(0);
202
203    /// A set containing all [`Attribute`]s
204    pub const ALL: AttributeSet = AttributeSet((1 << Attribute::COUNT) - 1);
205
206    /// Return a new set containing no [`Attribute`]s
207    pub fn new() -> AttributeSet {
208        AttributeSet(0)
209    }
210
211    /// Test whether the set is empty
212    pub fn is_empty(self) -> bool {
213        self.0 == 0
214    }
215
216    /// Returns the number of [`Attribute`]s in the set
217    pub fn len(self) -> usize {
218        let qty = self.0.count_ones();
219        match usize::try_from(qty) {
220            Ok(sz) => sz,
221            Err(_) => unreachable!("The number of bits in a u16 should fit in a usize"),
222        }
223    }
224
225    /// Test whether the set contains all [`Attribute`]s
226    pub fn is_all(self) -> bool {
227        self == Self::ALL
228    }
229
230    /// Test whether the set contains the given [`Attribute`]
231    pub fn contains(self, attr: Attribute) -> bool {
232        self.0 & (attr as u16) != 0
233    }
234
235    /// Adds the given [`Attribute`] to the set if not already present.
236    ///
237    /// Returns `true` if the given `Attribute` was not already in the set.
238    ///
239    /// # Example
240    ///
241    /// ```
242    /// use parse_style::{Attribute, AttributeSet};
243    ///
244    /// let mut attrset = AttributeSet::new();
245    /// assert!(!attrset.contains(Attribute::Bold));
246    /// assert!(attrset.insert(Attribute::Bold));
247    /// assert!(attrset.contains(Attribute::Bold));
248    /// assert!(!attrset.insert(Attribute::Bold));
249    /// assert!(attrset.contains(Attribute::Bold));
250    /// ```
251    pub fn insert(&mut self, attr: Attribute) -> bool {
252        let attr = attr as u16;
253        let adding = (self.0 & attr) == 0;
254        self.0 |= attr;
255        adding
256    }
257
258    /// Removes the given [`Attribute`] from the set if present.
259    ///
260    /// Returns `true` if the given `Attribute` was present in the set.
261    ///
262    /// # Example
263    ///
264    /// ```
265    /// use parse_style::{Attribute, AttributeSet};
266    ///
267    /// let mut attrset = AttributeSet::from([Attribute::Bold]);
268    /// assert!(attrset.contains(Attribute::Bold));
269    /// assert!(attrset.remove(Attribute::Bold));
270    /// assert!(!attrset.contains(Attribute::Bold));
271    /// assert!(!attrset.remove(Attribute::Bold));
272    /// assert!(!attrset.contains(Attribute::Bold));
273    /// ```
274    pub fn remove(&mut self, attr: Attribute) -> bool {
275        let attr = attr as u16;
276        let present = (self.0 & attr) != 0;
277        self.0 &= !attr;
278        present
279    }
280
281    /// Removes all [`Attribute`]s from the set
282    pub fn clear(&mut self) {
283        *self = Self::default();
284    }
285
286    /// Returns true if `self` and `other` are disjoint, i.e., if there is no
287    /// [`Attribute`] that is in both sets.
288    ///
289    /// # Examples
290    ///
291    /// ```
292    /// use parse_style::{Attribute, AttributeSet};
293    ///
294    /// let attrset1 = AttributeSet::from([Attribute::Bold,
295    /// Attribute::Italic]);
296    /// let attrset2 = AttributeSet::from([Attribute::Underline,
297    /// Attribute::Blink]);
298    /// assert!(attrset1.is_disjoint(attrset2));
299    /// assert!(attrset2.is_disjoint(attrset1));
300    /// ```
301    ///
302    /// ```
303    /// use parse_style::{Attribute, AttributeSet};
304    ///
305    /// let attrset1 = AttributeSet::from([Attribute::Bold,
306    /// Attribute::Italic]);
307    /// let attrset2 = AttributeSet::from([Attribute::Bold,
308    /// Attribute::Underline]);
309    /// assert!(!attrset1.is_disjoint(attrset2));
310    /// assert!(!attrset2.is_disjoint(attrset1));
311    /// ```
312    pub fn is_disjoint(self, other: AttributeSet) -> bool {
313        self.0 & other.0 == 0
314    }
315
316    /// Returns `true` if `self` is a subset of `other`
317    ///
318    /// # Examples
319    ///
320    /// ```
321    /// use parse_style::{Attribute, AttributeSet};
322    ///
323    /// let attrset1 = AttributeSet::from([Attribute::Bold]);
324    /// let attrset2 = AttributeSet::from([Attribute::Bold,
325    /// Attribute::Underline]);
326    /// assert!(attrset1.is_subset(attrset2));
327    /// assert!(!attrset2.is_subset(attrset1));
328    /// ```
329    ///
330    /// ```
331    /// use parse_style::{Attribute, AttributeSet};
332    ///
333    /// let attrset1 = AttributeSet::from([Attribute::Bold,
334    /// Attribute::Italic]);
335    /// let attrset2 = AttributeSet::from([Attribute::Bold,
336    /// Attribute::Underline]);
337    /// assert!(!attrset1.is_subset(attrset2));
338    /// assert!(!attrset2.is_subset(attrset1));
339    /// ```
340    pub fn is_subset(self, other: AttributeSet) -> bool {
341        self.0 & other.0 == self.0
342    }
343
344    /// Returns `true` if `self` is a superset of `other`
345    ///
346    /// # Examples
347    ///
348    /// ```
349    /// use parse_style::{Attribute, AttributeSet};
350    ///
351    /// let attrset1 = AttributeSet::from([Attribute::Bold]);
352    /// let attrset2 = AttributeSet::from([Attribute::Bold,
353    /// Attribute::Underline]);
354    /// assert!(!attrset1.is_superset(attrset2));
355    /// assert!(attrset2.is_superset(attrset1));
356    /// ```
357    ///
358    /// ```
359    /// use parse_style::{Attribute, AttributeSet};
360    ///
361    /// let attrset1 = AttributeSet::from([Attribute::Bold,
362    /// Attribute::Italic]);
363    /// let attrset2 = AttributeSet::from([Attribute::Bold,
364    /// Attribute::Underline]);
365    /// assert!(!attrset1.is_superset(attrset2));
366    /// assert!(!attrset2.is_superset(attrset1));
367    /// ```
368    pub fn is_superset(self, other: AttributeSet) -> bool {
369        self.0 & other.0 == other.0
370    }
371}
372
373impl From<Attribute> for AttributeSet {
374    fn from(value: Attribute) -> AttributeSet {
375        AttributeSet(value as u16)
376    }
377}
378
379impl<const N: usize> From<[Attribute; N]> for AttributeSet {
380    fn from(value: [Attribute; N]) -> AttributeSet {
381        AttributeSet::from_iter(value)
382    }
383}
384
385impl IntoIterator for AttributeSet {
386    type Item = Attribute;
387    type IntoIter = AttributeSetIter;
388
389    fn into_iter(self) -> AttributeSetIter {
390        AttributeSetIter::new(self)
391    }
392}
393
394impl FromIterator<Attribute> for AttributeSet {
395    fn from_iter<I: IntoIterator<Item = Attribute>>(iter: I) -> Self {
396        iter.into_iter()
397            .fold(AttributeSet::new(), |set, attr| set | attr)
398    }
399}
400
401impl Extend<Attribute> for AttributeSet {
402    fn extend<I: IntoIterator<Item = Attribute>>(&mut self, iter: I) {
403        for attr in iter {
404            *self |= attr;
405        }
406    }
407}
408
409#[cfg(feature = "anstyle")]
410#[cfg_attr(docsrs, doc(cfg(feature = "anstyle")))]
411impl From<AttributeSet> for anstyle::Effects {
412    /// Convert an `AttributeSet` to an [`anstyle::Effects`]
413    ///
414    /// # Data Loss
415    ///
416    /// The following attributes are discarded during conversion, as they have
417    /// no `anstyle::Effects` equivalents:
418    ///
419    /// - [`Attribute::Blink2`]
420    /// - [`Attribute::Frame`]
421    /// - [`Attribute::Encircle`]
422    /// - [`Attribute::Overline`]
423    fn from(value: AttributeSet) -> anstyle::Effects {
424        let mut efs = anstyle::Effects::new();
425        for attr in value {
426            match attr {
427                Attribute::Bold => efs |= anstyle::Effects::BOLD,
428                Attribute::Dim => efs |= anstyle::Effects::DIMMED,
429                Attribute::Italic => efs |= anstyle::Effects::ITALIC,
430                Attribute::Underline => efs |= anstyle::Effects::UNDERLINE,
431                Attribute::Blink => efs |= anstyle::Effects::BLINK,
432                Attribute::Blink2 => (),
433                Attribute::Reverse => efs |= anstyle::Effects::INVERT,
434                Attribute::Conceal => efs |= anstyle::Effects::HIDDEN,
435                Attribute::Strike => efs |= anstyle::Effects::STRIKETHROUGH,
436                Attribute::Underline2 => efs |= anstyle::Effects::DOUBLE_UNDERLINE,
437                Attribute::Frame => (),
438                Attribute::Encircle => (),
439                Attribute::Overline => (),
440            }
441        }
442        efs
443    }
444}
445
446#[cfg(feature = "anstyle")]
447#[cfg_attr(docsrs, doc(cfg(feature = "anstyle")))]
448impl From<anstyle::Effects> for AttributeSet {
449    /// Convert an [`anstyle::Effects`] to an `AttributeSet`
450    ///
451    ///
452    /// # Data Loss
453    ///
454    /// The following effects are discarded during conversion, as they have no
455    /// `Attribute` equivalents:
456    ///
457    /// - [`anstyle::Effects::CURLY_UNDERLINE`]
458    /// - [`anstyle::Effects::DOTTED_UNDERLINE`]
459    /// - [`anstyle::Effects::DASHED_UNDERLINE`]
460    fn from(value: anstyle::Effects) -> AttributeSet {
461        let mut set = AttributeSet::new();
462        for eff in value.iter() {
463            match eff {
464                anstyle::Effects::BOLD => set |= Attribute::Bold,
465                anstyle::Effects::DIMMED => set |= Attribute::Dim,
466                anstyle::Effects::ITALIC => set |= Attribute::Italic,
467                anstyle::Effects::UNDERLINE => set |= Attribute::Underline,
468                anstyle::Effects::DOUBLE_UNDERLINE => set |= Attribute::Underline2,
469                anstyle::Effects::CURLY_UNDERLINE => (),
470                anstyle::Effects::DOTTED_UNDERLINE => (),
471                anstyle::Effects::DASHED_UNDERLINE => (),
472                anstyle::Effects::BLINK => set |= Attribute::Blink,
473                anstyle::Effects::INVERT => set |= Attribute::Reverse,
474                anstyle::Effects::HIDDEN => set |= Attribute::Conceal,
475                anstyle::Effects::STRIKETHROUGH => set |= Attribute::Strike,
476                // Because an `Effects` can be either a single effect or
477                // multiple, we need a catch-all arm here, even though the
478                // iterator will only yield single effects.
479                _ => (),
480            }
481        }
482        set
483    }
484}
485
486#[cfg(feature = "crossterm")]
487#[cfg_attr(docsrs, doc(cfg(feature = "crossterm")))]
488impl From<AttributeSet> for crossterm::style::Attributes {
489    /// Convert an `AttributeSet` to a [`crossterm::style::Attributes`] that
490    /// enables the input attributes
491    fn from(value: AttributeSet) -> crossterm::style::Attributes {
492        use crossterm::style::Attribute as CrossAttrib;
493        let mut attributes = crossterm::style::Attributes::none();
494        for attr in value {
495            let ca = match attr {
496                Attribute::Bold => CrossAttrib::Bold,
497                Attribute::Dim => CrossAttrib::Dim,
498                Attribute::Italic => CrossAttrib::Italic,
499                Attribute::Underline => CrossAttrib::Underlined,
500                Attribute::Blink => CrossAttrib::SlowBlink,
501                Attribute::Blink2 => CrossAttrib::RapidBlink,
502                Attribute::Reverse => CrossAttrib::Reverse,
503                Attribute::Conceal => CrossAttrib::Hidden,
504                Attribute::Strike => CrossAttrib::CrossedOut,
505                Attribute::Underline2 => CrossAttrib::DoubleUnderlined,
506                Attribute::Frame => CrossAttrib::Framed,
507                Attribute::Encircle => CrossAttrib::Encircled,
508                Attribute::Overline => CrossAttrib::OverLined,
509            };
510            attributes.set(ca);
511        }
512        attributes
513    }
514}
515
516#[cfg(feature = "ratatui")]
517#[cfg_attr(docsrs, doc(cfg(feature = "ratatui")))]
518impl From<AttributeSet> for ratatui_core::style::Modifier {
519    /// Convert an `AttributeSet` to a [`ratatui_core::style::Modifier`]
520    ///
521    /// # Data Loss
522    ///
523    /// The following attributes are discarded during conversion, as they have
524    /// no `ratatui_core::style::Modifier` equivalents:
525    ///
526    /// - [`Attribute::Underline2`]
527    /// - [`Attribute::Frame`]
528    /// - [`Attribute::Encircle`]
529    /// - [`Attribute::Overline`]
530    fn from(value: AttributeSet) -> ratatui_core::style::Modifier {
531        let mut mods = ratatui_core::style::Modifier::empty();
532        for attr in value {
533            match attr {
534                Attribute::Bold => mods |= ratatui_core::style::Modifier::BOLD,
535                Attribute::Dim => mods |= ratatui_core::style::Modifier::DIM,
536                Attribute::Italic => mods |= ratatui_core::style::Modifier::ITALIC,
537                Attribute::Underline => mods |= ratatui_core::style::Modifier::UNDERLINED,
538                Attribute::Blink => mods |= ratatui_core::style::Modifier::SLOW_BLINK,
539                Attribute::Blink2 => mods |= ratatui_core::style::Modifier::RAPID_BLINK,
540                Attribute::Reverse => mods |= ratatui_core::style::Modifier::REVERSED,
541                Attribute::Conceal => mods |= ratatui_core::style::Modifier::HIDDEN,
542                Attribute::Strike => mods |= ratatui_core::style::Modifier::CROSSED_OUT,
543                Attribute::Underline2 => (),
544                Attribute::Frame => (),
545                Attribute::Encircle => (),
546                Attribute::Overline => (),
547            }
548        }
549        mods
550    }
551}
552
553#[cfg(feature = "ratatui")]
554#[cfg_attr(docsrs, doc(cfg(feature = "ratatui")))]
555impl From<ratatui_core::style::Modifier> for AttributeSet {
556    /// Convert a [`ratatui_core::style::Modifier`] to an `AttributeSet`
557    fn from(value: ratatui_core::style::Modifier) -> AttributeSet {
558        let mut set = AttributeSet::new();
559        for m in value.iter() {
560            match m {
561                ratatui_core::style::Modifier::BOLD => set |= Attribute::Bold,
562                ratatui_core::style::Modifier::DIM => set |= Attribute::Dim,
563                ratatui_core::style::Modifier::ITALIC => set |= Attribute::Italic,
564                ratatui_core::style::Modifier::UNDERLINED => set |= Attribute::Underline,
565                ratatui_core::style::Modifier::SLOW_BLINK => set |= Attribute::Blink,
566                ratatui_core::style::Modifier::RAPID_BLINK => set |= Attribute::Blink,
567                ratatui_core::style::Modifier::REVERSED => set |= Attribute::Reverse,
568                ratatui_core::style::Modifier::HIDDEN => set |= Attribute::Conceal,
569                ratatui_core::style::Modifier::CROSSED_OUT => set |= Attribute::Strike,
570                // Because a `Modifier` can be either a single effect or
571                // multiple, we need a catch-all arm here, even though the
572                // iterator will only yield single modifiers.
573                _ => (),
574            }
575        }
576        set
577    }
578}
579
580impl<A: Into<AttributeSet>> std::ops::BitAnd<A> for AttributeSet {
581    type Output = AttributeSet;
582
583    fn bitand(self, rhs: A) -> AttributeSet {
584        AttributeSet(self.0 & rhs.into().0)
585    }
586}
587
588impl<A: Into<AttributeSet>> std::ops::BitAndAssign<A> for AttributeSet {
589    fn bitand_assign(&mut self, rhs: A) {
590        self.0 &= rhs.into().0;
591    }
592}
593
594impl<A: Into<AttributeSet>> std::ops::BitOr<A> for AttributeSet {
595    type Output = AttributeSet;
596
597    fn bitor(self, rhs: A) -> AttributeSet {
598        AttributeSet(self.0 | rhs.into().0)
599    }
600}
601
602impl<A: Into<AttributeSet>> std::ops::BitOrAssign<A> for AttributeSet {
603    fn bitor_assign(&mut self, rhs: A) {
604        self.0 |= rhs.into().0;
605    }
606}
607
608impl<A: Into<AttributeSet>> std::ops::BitXor<A> for AttributeSet {
609    type Output = AttributeSet;
610
611    fn bitxor(self, rhs: A) -> AttributeSet {
612        AttributeSet(self.0 ^ rhs.into().0)
613    }
614}
615
616impl<A: Into<AttributeSet>> std::ops::BitXorAssign<A> for AttributeSet {
617    fn bitxor_assign(&mut self, rhs: A) {
618        self.0 ^= rhs.into().0;
619    }
620}
621
622impl<A: Into<AttributeSet>> std::ops::Sub<A> for AttributeSet {
623    type Output = AttributeSet;
624
625    fn sub(self, rhs: A) -> AttributeSet {
626        AttributeSet(self.0 & !rhs.into().0)
627    }
628}
629
630impl<A: Into<AttributeSet>> std::ops::SubAssign<A> for AttributeSet {
631    fn sub_assign(&mut self, rhs: A) {
632        self.0 &= !rhs.into().0;
633    }
634}
635
636impl std::ops::Not for AttributeSet {
637    type Output = AttributeSet;
638
639    fn not(self) -> AttributeSet {
640        AttributeSet(!self.0 & ((1 << Attribute::COUNT) - 1))
641    }
642}
643
644/// An iterator over the [`Attribute`]s in an [`AttributeSet`]
645#[derive(Clone, Debug)]
646pub struct AttributeSetIter {
647    inner: AttributeIter,
648    set: AttributeSet,
649}
650
651impl AttributeSetIter {
652    fn new(set: AttributeSet) -> AttributeSetIter {
653        AttributeSetIter {
654            inner: Attribute::iter(),
655            set,
656        }
657    }
658}
659
660impl Iterator for AttributeSetIter {
661    type Item = Attribute;
662
663    fn next(&mut self) -> Option<Attribute> {
664        self.inner.by_ref().find(|&attr| self.set.contains(attr))
665    }
666
667    fn size_hint(&self) -> (usize, Option<usize>) {
668        (0, self.inner.size_hint().1)
669    }
670}
671
672impl DoubleEndedIterator for AttributeSetIter {
673    fn next_back(&mut self) -> Option<Attribute> {
674        self.inner.by_ref().rfind(|&attr| self.set.contains(attr))
675    }
676}
677
678impl std::iter::FusedIterator for AttributeSetIter {}
679
680/// Error returned when parsing an attribute fails
681#[derive(Clone, Debug, Eq, Error, PartialEq)]
682#[error("invalid attribute name: {0:?}")]
683pub struct ParseAttributeError(
684    /// The invalid attribute string
685    pub String,
686);
687
688#[cfg(test)]
689mod tests {
690    use super::*;
691
692    mod attribute {
693        use super::*;
694        use rstest::rstest;
695
696        #[rstest]
697        #[case(Attribute::Bold, "bold")]
698        #[case(Attribute::Dim, "dim")]
699        #[case(Attribute::Italic, "italic")]
700        #[case(Attribute::Underline, "underline")]
701        #[case(Attribute::Blink, "blink")]
702        #[case(Attribute::Blink2, "blink2")]
703        #[case(Attribute::Reverse, "reverse")]
704        #[case(Attribute::Conceal, "conceal")]
705        #[case(Attribute::Strike, "strike")]
706        #[case(Attribute::Underline2, "underline2")]
707        #[case(Attribute::Frame, "frame")]
708        #[case(Attribute::Encircle, "encircle")]
709        #[case(Attribute::Overline, "overline")]
710        fn display(#[case] attr: Attribute, #[case] s: &str) {
711            assert_eq!(attr.to_string(), s);
712        }
713
714        #[rstest]
715        #[case(Attribute::Bold, "b")]
716        #[case(Attribute::Dim, "d")]
717        #[case(Attribute::Italic, "i")]
718        #[case(Attribute::Underline, "u")]
719        #[case(Attribute::Blink, "blink")]
720        #[case(Attribute::Blink2, "blink2")]
721        #[case(Attribute::Reverse, "r")]
722        #[case(Attribute::Conceal, "c")]
723        #[case(Attribute::Strike, "s")]
724        #[case(Attribute::Underline2, "uu")]
725        #[case(Attribute::Frame, "frame")]
726        #[case(Attribute::Encircle, "encircle")]
727        #[case(Attribute::Overline, "overline")]
728        fn alt_display(#[case] attr: Attribute, #[case] s: &str) {
729            assert_eq!(format!("{attr:#}"), s);
730        }
731    }
732
733    mod attribute_set {
734        use super::*;
735
736        #[test]
737        fn double_ended_iteration() {
738            let attrs = Attribute::Bold | Attribute::Frame | Attribute::Reverse | Attribute::Strike;
739            let mut iter = attrs.into_iter();
740            assert_eq!(iter.next(), Some(Attribute::Bold));
741            assert_eq!(iter.next_back(), Some(Attribute::Frame));
742            assert_eq!(iter.next(), Some(Attribute::Reverse));
743            assert_eq!(iter.next_back(), Some(Attribute::Strike));
744            assert_eq!(iter.next(), None);
745            assert_eq!(iter.next_back(), None);
746            assert_eq!(iter.next(), None);
747            assert_eq!(iter.next_back(), None);
748        }
749    }
750}