1use std::io;
8
9use crate::Level;
10
11#[allow(missing_docs)]
13#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
14pub enum Color {
15 Black,
16 Red,
17 Green,
18 Yellow,
19 Blue,
20 Magenta,
21 Cyan,
22 White,
23}
24
25impl Color {
26 #[must_use]
28 pub(crate) fn fg_code(&self) -> &'static str {
29 match self {
30 Color::Black => "\x1b[30m",
31 Color::Red => "\x1b[31m",
32 Color::Green => "\x1b[32m",
33 Color::Yellow => "\x1b[33m",
34 Color::Blue => "\x1b[34m",
35 Color::Magenta => "\x1b[35m",
36 Color::Cyan => "\x1b[36m",
37 Color::White => "\x1b[37m",
38 }
39 }
40
41 #[must_use]
43 pub(crate) fn bg_code(&self) -> &'static str {
44 match self {
45 Color::Black => "\x1b[40m",
46 Color::Red => "\x1b[41m",
47 Color::Green => "\x1b[42m",
48 Color::Yellow => "\x1b[43m",
49 Color::Blue => "\x1b[44m",
50 Color::Magenta => "\x1b[45m",
51 Color::Cyan => "\x1b[46m",
52 Color::White => "\x1b[47m",
53 }
54 }
55}
56
57#[derive(Clone, Eq, PartialEq, Hash, Debug)]
59pub struct Style {
60 color: Option<Color>,
61 bg_color: Option<Color>,
62 bold: bool,
63 faint: bool,
64 italic: bool,
65 underline: bool,
66 slow_blink: bool,
67 rapid_blink: bool,
68 invert: bool,
69 conceal: bool,
70 strikethrough: bool,
71 reset: bool,
72}
73
74impl Style {
75 #[allow(clippy::new_without_default)]
77 #[deprecated(
78 since = "0.3.0",
79 note = "it may be removed in the future, use `Style::builder()` instead"
80 )]
81 #[must_use]
82 pub fn new() -> Style {
83 Style::builder().build()
84 }
85
86 #[must_use]
88 pub fn builder() -> StyleBuilder {
89 StyleBuilder {
90 style: Style {
91 color: None,
92 bg_color: None,
93 bold: false,
94 faint: false,
95 italic: false,
96 underline: false,
97 slow_blink: false,
98 rapid_blink: false,
99 invert: false,
100 conceal: false,
101 strikethrough: false,
102 reset: false,
103 },
104 }
105 }
106
107 pub(crate) fn write_start(&self, dest: &mut impl io::Write) -> io::Result<()> {
108 if self.reset {
109 dest.write_all(Self::reset_code().as_bytes())?;
110 return Ok(());
111 }
112 if let Some(color) = self.color {
113 dest.write_all(color.fg_code().as_bytes())?;
114 }
115 if let Some(color) = self.bg_color {
116 dest.write_all(color.bg_code().as_bytes())?;
117 }
118 if self.bold {
119 dest.write_all("\x1b[1m".as_bytes())?;
120 }
121 if self.faint {
122 dest.write_all("\x1b[2m".as_bytes())?;
123 }
124 if self.italic {
125 dest.write_all("\x1b[3m".as_bytes())?;
126 }
127 if self.underline {
128 dest.write_all("\x1b[4m".as_bytes())?;
129 }
130 if self.slow_blink {
131 dest.write_all("\x1b[5m".as_bytes())?;
132 }
133 if self.rapid_blink {
134 dest.write_all("\x1b[6m".as_bytes())?;
135 }
136 if self.invert {
137 dest.write_all("\x1b[7m".as_bytes())?;
138 }
139 if self.conceal {
140 dest.write_all("\x1b[8m".as_bytes())?;
141 }
142 if self.strikethrough {
143 dest.write_all("\x1b[9m".as_bytes())?;
144 }
145 Ok(())
146 }
147
148 pub(crate) fn write_end(&self, dest: &mut impl io::Write) -> io::Result<()> {
149 dest.write_all(Self::reset_code().as_bytes())
150 }
151
152 #[must_use]
153 fn reset_code() -> &'static str {
154 "\x1b[m"
155 }
156}
157
158#[allow(missing_docs)]
159#[derive(Clone, Eq, PartialEq, Hash, Debug)]
160pub struct StyleBuilder {
161 style: Style,
162}
163
164pub(crate) mod macros {
165 macro_rules! impl_style_builder_setters {
166 ($builder_type:ident =>) => {};
167 ($builder_type:ident => $visibility:vis $field_name:ident: Option<$field_type:ty>, $($tail:tt)*) => {
168 #[allow(missing_docs)]
169 $visibility fn $field_name(&mut self, $field_name: $field_type) -> &mut $builder_type {
170 self.style.$field_name = Some($field_name);
171 self
172 }
173 macros::impl_style_builder_setters! { $builder_type => $($tail)* }
174 };
175 ($builder_type:ident => $visibility:vis $field_name:ident: bool, $($tail:tt)*) => {
176 #[allow(missing_docs)]
177 $visibility fn $field_name(&mut self) -> &mut $builder_type {
178 self.style.$field_name = true;
179 self
180 }
181 macros::impl_style_builder_setters! { $builder_type => $($tail)* }
182 };
183 }
184 pub(crate) use impl_style_builder_setters;
185}
186
187impl StyleBuilder {
188 #[allow(clippy::new_without_default)]
190 #[deprecated(
191 since = "0.3.0",
192 note = "it may be removed in the future, use `Style::builder()` instead"
193 )]
194 #[must_use]
195 pub fn new() -> StyleBuilder {
196 Style::builder()
197 }
198
199 macros::impl_style_builder_setters! {
200 StyleBuilder =>
201 pub reset: bool,
202 pub color: Option<Color>,
203 pub bg_color: Option<Color>,
204 pub bold: bool,
205 pub faint: bool,
206 pub italic: bool,
207 pub underline: bool,
208 pub slow_blink: bool,
209 pub rapid_blink: bool,
210 pub invert: bool,
211 pub conceal: bool,
212 pub strikethrough: bool,
213 }
214
215 #[must_use]
217 pub fn build(&mut self) -> Style {
218 self.style.clone()
219 }
220}
221
222#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
224pub enum StyleMode {
225 Always,
227 Auto,
230 Never,
232}
233
234#[derive(Clone, Eq, PartialEq, Hash, Debug)]
235pub(crate) struct LevelStyles([Style; Level::count()]);
236
237impl LevelStyles {
238 #[allow(dead_code)]
239 #[must_use]
240 pub(crate) fn style(&self, level: Level) -> &Style {
241 &self.0[level as usize]
242 }
243
244 #[allow(dead_code)]
245 pub(crate) fn set_style(&mut self, level: Level, style: Style) {
246 self.0[level as usize] = style;
247 }
248}
249
250impl Default for LevelStyles {
251 fn default() -> LevelStyles {
252 LevelStyles([
253 Style::builder().bg_color(Color::Red).bold().build(), Style::builder().color(Color::Red).bold().build(), Style::builder().color(Color::Yellow).bold().build(), Style::builder().color(Color::Green).build(), Style::builder().color(Color::Cyan).build(), Style::builder().color(Color::White).build(), ])
260 }
261}