use core::fmt::{Display, Formatter, Result, Write};
use crate::{
ColorTarget, Effect, Reset, UnderlineEffect,
colors::{Color, WriteColorCodes as _},
impl_macros::{
additive_styling::impl_additive_styling_type,
composed_styling::impl_composed_styling_methods,
},
style::encoded_effects::EncodedEffects,
};
pub use encoded_effects::*;
mod encoded_effects;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Style {
encoded_effects: EncodedEffects,
fg: Option<Color>,
bg: Option<Color>,
underline_color: Option<Color>,
}
impl Style {
#[must_use]
pub const fn new() -> Self {
Style {
encoded_effects: EncodedEffects::new(),
fg: None,
bg: None,
underline_color: None,
}
}
impl_composed_styling_methods! {
args: [self, effect, underline_effect, target, color, value];
example_variable: r"style";
set_effect: {
let effect = effect.into();
let encoded_effects = self.encoded_effects.set(effect, value);
Self {
encoded_effects,
..self
}
}
get_effect: {
let effect = effect.into();
self.encoded_effects.get(effect)
}
get_effects: {
self.encoded_effects.get_effects()
}
set_underline_effect: {
let encoded_effects = self.encoded_effects.set_underline(underline_effect);
Self {
encoded_effects,
..self
}
}
get_underline_effect: {
UnderlineEffect::all().find(|&underline_effect| self.get_effect(underline_effect))
}
set_color: {
let color = color.map(Into::into);
match target {
ColorTarget::Foreground => Self { fg: color, ..self },
ColorTarget::Background => Self { bg: color, ..self },
ColorTarget::Underline => Self {
underline_color: color,
..self
},
}
}
get_color: {
match target {
ColorTarget::Foreground => self.fg,
ColorTarget::Background => self.bg,
ColorTarget::Underline => self.underline_color,
}
}
}
}
impl_additive_styling_type!(Style {
args: [self];
to_style: SELF
});
impl Display for Style {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
if *self == Style::new() {
write_escape_sequence(f, 0)
} else {
struct Codes(Style);
impl Display for Codes {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let mut code_writer = CodeWriter { f, any: false };
for effect in Effect::all() {
if self.0.get_effect(effect) {
effect.write_codes(&mut code_writer)?;
}
}
if let Some(color) = self.0.fg {
color.write_color_codes(ColorTarget::Foreground, &mut code_writer)?;
}
if let Some(color) = self.0.bg {
color.write_color_codes(ColorTarget::Background, &mut code_writer)?;
}
if let Some(color) = self.0.underline_color {
color.write_color_codes(ColorTarget::Underline, &mut code_writer)?;
}
Ok(())
}
}
write_escape_sequence(f, Codes(*self))
}
}
}
impl PartialEq<Reset> for Style {
fn eq(&self, other: &Reset) -> bool {
*self == other.to_style()
}
}
pub(crate) struct CodeWriter<'a, 'b> {
f: &'a mut Formatter<'b>,
any: bool,
}
impl CodeWriter<'_, '_> {
pub(crate) fn write_code(&mut self, code: impl Display) -> Result {
if self.any {
self.f.write_char(';')?;
}
write!(self.f, "{code}")?;
self.any = true;
Ok(())
}
}
fn write_escape_sequence(f: &mut impl Write, codes: impl Display) -> Result {
write!(f, "\x1b[{codes}m")
}