/// A set of text effects /// /// # Examples /// /// ```rust /// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE; /// ``` #[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Effects(u16); impl Effects { const PLAIN: Self = Effects(0); pub const BOLD: Self = Effects(1 << 0); pub const DIMMED: Self = Effects(1 << 1); /// Not widely supported. Sometimes treated as inverse or blink pub const ITALIC: Self = Effects(1 << 2); /// Style extensions exist for Kitty, VTE, mintty and iTerm2. pub const UNDERLINE: Self = Effects(1 << 3); pub const DOUBLE_UNDERLINE: Self = Effects(1 << 4); pub const CURLY_UNDERLINE: Self = Effects(1 << 5); pub const DOTTED_UNDERLINE: Self = Effects(1 << 6); pub const DASHED_UNDERLINE: Self = Effects(1 << 7); pub const BLINK: Self = Effects(1 << 8); /// Swap foreground and background colors; inconsistent emulation pub const INVERT: Self = Effects(1 << 9); pub const HIDDEN: Self = Effects(1 << 10); /// Characters legible but marked as if for deletion. Not supported in Terminal.app pub const STRIKETHROUGH: Self = Effects(1 << 11); /// No effects enabled /// /// # Examples /// /// ```rust /// let effects = anstyle::Effects::new(); /// ``` #[inline] pub const fn new() -> Self { Self::PLAIN } /// Check if no effects are enabled /// /// # Examples /// /// ```rust /// let effects = anstyle::Effects::new(); /// assert!(effects.is_plain()); /// /// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE; /// assert!(!effects.is_plain()); /// ``` #[inline] pub const fn is_plain(self) -> bool { self.0 == Self::PLAIN.0 } /// Returns `true` if all of the effects in `other` are contained within `self`. /// /// # Examples /// /// ```rust /// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE; /// assert!(effects.contains(anstyle::Effects::BOLD)); /// /// let effects = anstyle::Effects::new(); /// assert!(!effects.contains(anstyle::Effects::BOLD)); /// ``` #[inline(always)] pub const fn contains(self, other: Effects) -> bool { (other.0 & self.0) == other.0 } /// Inserts the specified effects in-place. /// /// # Examples /// /// ```rust /// let effects = anstyle::Effects::new().insert(anstyle::Effects::new()); /// assert!(effects.is_plain()); /// /// let effects = anstyle::Effects::new().insert(anstyle::Effects::BOLD); /// assert!(effects.contains(anstyle::Effects::BOLD)); /// ``` #[inline(always)] #[must_use] pub const fn insert(mut self, other: Effects) -> Self { self.0 |= other.0; self } /// Removes the specified effects in-place. /// /// # Examples /// /// ```rust /// let effects = (anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE).remove(anstyle::Effects::BOLD); /// assert!(!effects.contains(anstyle::Effects::BOLD)); /// assert!(effects.contains(anstyle::Effects::UNDERLINE)); /// ``` #[inline(always)] #[must_use] pub const fn remove(mut self, other: Effects) -> Self { self.0 &= !other.0; self } /// Reset all effects in-place /// ```rust /// let effects = (anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE).clear(); /// assert!(!effects.contains(anstyle::Effects::BOLD)); /// assert!(!effects.contains(anstyle::Effects::UNDERLINE)); /// ``` #[inline(always)] #[must_use] pub const fn clear(self) -> Self { Self::new() } /// Enable or disable the specified effects depending on the passed value. /// /// # Examples /// /// ```rust /// let effects = anstyle::Effects::new().set(anstyle::Effects::BOLD, true); /// assert!(effects.contains(anstyle::Effects::BOLD)); /// ``` #[inline] #[must_use] pub const fn set(self, other: Self, enable: bool) -> Self { if enable { self.insert(other) } else { self.remove(other) } } /// Iterate over enabled effects #[inline(always)] pub fn iter(self) -> EffectIter { EffectIter { index: 0, effects: self, } } /// Iterate over enabled effect indices #[inline(always)] pub(crate) fn index_iter(self) -> EffectIndexIter { EffectIndexIter { index: 0, effects: self, } } /// Render the ANSI code #[inline] pub fn render(self) -> impl core::fmt::Display + Copy + Clone { EffectsDisplay(self) } #[inline] #[cfg(feature = "std")] pub(crate) fn write_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> { for index in self.index_iter() { write.write_all(METADATA[index].escape.as_bytes())?; } Ok(()) } } /// # Examples /// /// ```rust /// let effects = anstyle::Effects::new(); /// assert_eq!(format!("{:?}", effects), "Effects()"); /// /// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE; /// assert_eq!(format!("{:?}", effects), "Effects(BOLD | UNDERLINE)"); /// ``` impl core::fmt::Debug for Effects { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "Effects(")?; for (i, index) in self.index_iter().enumerate() { if i != 0 { write!(f, " | ")?; } write!(f, "{}", METADATA[index].name)?; } write!(f, ")")?; Ok(()) } } /// # Examples /// /// ```rust /// let effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE; /// assert_eq!(format!("{:?}", effects), "Effects(BOLD | UNDERLINE)"); /// ``` impl core::ops::BitOr for Effects { type Output = Self; #[inline(always)] fn bitor(self, rhs: Self) -> Self { self.insert(rhs) } } /// # Examples /// /// ```rust /// let mut effects = anstyle::Effects::BOLD; /// effects |= anstyle::Effects::UNDERLINE; /// assert_eq!(format!("{:?}", effects), "Effects(BOLD | UNDERLINE)"); /// ``` impl core::ops::BitOrAssign for Effects { #[inline] fn bitor_assign(&mut self, other: Self) { *self = self.insert(other); } } /// # Examples /// /// ```rust /// let effects = (anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE) - anstyle::Effects::BOLD; /// assert_eq!(format!("{:?}", effects), "Effects(UNDERLINE)"); /// ``` impl core::ops::Sub for Effects { type Output = Self; #[inline] fn sub(self, other: Self) -> Self { self.remove(other) } } /// # Examples /// /// ```rust /// let mut effects = anstyle::Effects::BOLD | anstyle::Effects::UNDERLINE; /// effects -= anstyle::Effects::BOLD; /// assert_eq!(format!("{:?}", effects), "Effects(UNDERLINE)"); /// ``` impl core::ops::SubAssign for Effects { #[inline] fn sub_assign(&mut self, other: Self) { *self = self.remove(other); } } pub(crate) struct Metadata { pub(crate) name: &'static str, pub(crate) escape: &'static str, } pub(crate) const METADATA: [Metadata; 12] = [ Metadata { name: "BOLD", escape: escape!("1"), }, Metadata { name: "DIMMED", escape: escape!("2"), }, Metadata { name: "ITALIC", escape: escape!("3"), }, Metadata { name: "UNDERLINE", escape: escape!("4"), }, Metadata { name: "DOUBLE_UNDERLINE", escape: escape!("21"), }, Metadata { name: "CURLY_UNDERLINE", escape: escape!("4:3"), }, Metadata { name: "DOTTED_UNDERLINE", escape: escape!("4:4"), }, Metadata { name: "DASHED_UNDERLINE", escape: escape!("4:5"), }, Metadata { name: "BLINK", escape: escape!("5"), }, Metadata { name: "INVERT", escape: escape!("7"), }, Metadata { name: "HIDDEN", escape: escape!("8"), }, Metadata { name: "STRIKETHROUGH", escape: escape!("9"), }, ]; #[derive(Copy, Clone, Default, Debug)] struct EffectsDisplay(Effects); impl core::fmt::Display for EffectsDisplay { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { for index in self.0.index_iter() { METADATA[index].escape.fmt(f)?; } Ok(()) } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct EffectIter { index: usize, effects: Effects, } impl Iterator for EffectIter { type Item = Effects; fn next(&mut self) -> Option { while self.index < METADATA.len() { let index = self.index; self.index += 1; let effect = Effects(1 << index); if self.effects.contains(effect) { return Some(effect); } } None } } #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct EffectIndexIter { index: usize, effects: Effects, } impl Iterator for EffectIndexIter { type Item = usize; fn next(&mut self) -> Option { while self.index < METADATA.len() { let index = self.index; self.index += 1; let effect = Effects(1 << index); if self.effects.contains(effect) { return Some(index); } } None } }