fluent_ansi/
style.rs

1use core::fmt::{Display, Formatter, Result, Write};
2
3use crate::{
4    ColorTarget, Effect, Reset, UnderlineEffect,
5    colors::{Color, WriteColorCodes as _},
6    impl_macros::{
7        additive_styling::impl_additive_styling_type,
8        composed_styling::impl_composed_styling_methods,
9    },
10    style::encoded_effects::EncodedEffects,
11};
12
13pub use encoded_effects::*;
14
15mod encoded_effects;
16
17/// A structure representing text styling with effects and colors.
18#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
19pub struct Style {
20    encoded_effects: EncodedEffects,
21    fg: Option<Color>,
22    bg: Option<Color>,
23    underline_color: Option<Color>,
24}
25
26impl Style {
27    /// Creates a new, empty `Style` value.
28    #[must_use]
29    pub const fn new() -> Self {
30        Style {
31            encoded_effects: EncodedEffects::new(),
32            fg: None,
33            bg: None,
34            underline_color: None,
35        }
36    }
37
38    impl_composed_styling_methods! {
39        args: [self, effect, underline_effect, target, color, value];
40        example_variable: r"style";
41
42        set_effect: {
43            let effect = effect.into();
44            let encoded_effects = self.encoded_effects.set(effect, value);
45            Self {
46                encoded_effects,
47                ..self
48            }
49        }
50
51        get_effect: {
52            let effect = effect.into();
53            self.encoded_effects.get(effect)
54        }
55
56        get_effects: {
57            self.encoded_effects.get_effects()
58        }
59
60        set_underline_effect: {
61            let encoded_effects = self.encoded_effects.set_underline(underline_effect);
62            Self {
63                encoded_effects,
64                ..self
65            }
66        }
67
68        get_underline_effect: {
69            UnderlineEffect::all().find(|&underline_effect| self.get_effect(underline_effect))
70        }
71
72        set_color: {
73            let color = color.map(Into::into);
74            match target {
75                ColorTarget::Foreground => Self { fg: color, ..self },
76                ColorTarget::Background => Self { bg: color, ..self },
77                ColorTarget::Underline => Self {
78                    underline_color: color,
79                    ..self
80                },
81            }
82        }
83
84        get_color: {
85            match target {
86                ColorTarget::Foreground => self.fg,
87                ColorTarget::Background => self.bg,
88                ColorTarget::Underline => self.underline_color,
89            }
90        }
91    }
92}
93
94impl_additive_styling_type!(Style {
95    args: [self];
96    to_style: SELF
97});
98
99impl Display for Style {
100    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
101        if *self == Style::new() {
102            write_escape_sequence(f, 0)
103        } else {
104            struct Codes(Style);
105            impl Display for Codes {
106                fn fmt(&self, f: &mut Formatter<'_>) -> Result {
107                    let mut code_writer = CodeWriter { f, any: false };
108
109                    for effect in Effect::all() {
110                        if self.0.get_effect(effect) {
111                            effect.write_codes(&mut code_writer)?;
112                        }
113                    }
114                    if let Some(color) = self.0.fg {
115                        color.write_color_codes(ColorTarget::Foreground, &mut code_writer)?;
116                    }
117                    if let Some(color) = self.0.bg {
118                        color.write_color_codes(ColorTarget::Background, &mut code_writer)?;
119                    }
120                    if let Some(color) = self.0.underline_color {
121                        color.write_color_codes(ColorTarget::Underline, &mut code_writer)?;
122                    }
123                    Ok(())
124                }
125            }
126            write_escape_sequence(f, Codes(*self))
127        }
128    }
129}
130
131impl PartialEq<Reset> for Style {
132    fn eq(&self, other: &Reset) -> bool {
133        *self == other.to_style()
134    }
135}
136
137pub(crate) struct CodeWriter<'a, 'b> {
138    f: &'a mut Formatter<'b>,
139    any: bool,
140}
141
142impl CodeWriter<'_, '_> {
143    pub(crate) fn write_code(&mut self, code: impl Display) -> Result {
144        if self.any {
145            self.f.write_char(';')?;
146        }
147        write!(self.f, "{code}")?;
148        self.any = true;
149        Ok(())
150    }
151}
152
153fn write_escape_sequence(f: &mut impl Write, codes: impl Display) -> Result {
154    write!(f, "\x1b[{codes}m")
155}