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#[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 #[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}