/// 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 {
/// No [`Effects`] applied
const PLAIN: Self = Effects(0 );
#[ allow(missing_docs)]
pub const BOLD: Self = Effects(1 << 0 );
#[ allow(missing_docs)]
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 );
#[ allow(missing_docs)]
pub const DOUBLE_UNDERLINE: Self = Effects(1 << 4 );
#[ allow(missing_docs)]
pub const CURLY_UNDERLINE: Self = Effects(1 << 5 );
#[ allow(missing_docs)]
pub const DOTTED_UNDERLINE: Self = Effects(1 << 6 );
#[ allow(missing_docs)]
pub const DASHED_UNDERLINE: Self = Effects(1 << 7 );
#[ allow(missing_docs)]
pub const BLINK: Self = Effects(1 << 8 );
/// Swap foreground and background colors; inconsistent emulation
pub const INVERT: Self = Effects(1 << 9 );
#[ allow(missing_docs)]
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 {
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() {
let escape = METADATA[index].escape;
write!(f, "{escape}" )?;
}
Ok(())
}
}
/// Enumerate each enabled value in [`Effects`]
#[ derive(Clone, Debug, PartialEq, Eq)]
pub struct EffectIter {
index: usize,
effects: Effects,
}
impl Iterator for EffectIter {
type Item = Effects;
fn next(&mut self ) -> Option<Self ::Item> {
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<Self ::Item> {
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
}
}
#[ cfg(test)]
#[ cfg(feature = "std" )]
mod test {
use super ::*;
#[ test]
fn print_size_of() {
use std::mem::size_of;
dbg!(size_of::<Effects>());
dbg!(size_of::<EffectsDisplay>());
}
#[ test]
fn no_align() {
#[ track_caller]
fn assert_no_align(d: impl core::fmt::Display) {
let expected = format!("{d}" );
let actual = format!("{d:<10}" );
assert_eq!(expected, actual);
}
assert_no_align(Effects::BOLD.render());
}
}
Messung V0.5 in Prozent C=89 H=94 G=91
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-18)
¤
*© Formatika GbR, Deutschland