1use std::fmt::{Display, Formatter};
6use std::io::Write;
7use std::{fmt, io};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum Color {
11 Black,
12 Red,
13 Green,
14 Yellow,
15 Blue,
16 Magenta,
17 Cyan,
18 White,
19 BrightBlack,
20 BrightRed,
21 BrightGreen,
22 BrightYellow,
23 BrightBlue,
24 BrightMagenta,
25 BrightCyan,
26 BrightWhite,
27}
28
29impl Color {
30 #[must_use]
31 pub const fn code(&self) -> u8 {
32 match self {
33 Self::Black => 30,
34 Self::Red => 31,
35 Self::Green => 32,
36 Self::Yellow => 33,
37 Self::Blue => 34,
38 Self::Magenta => 35,
39 Self::Cyan => 36,
40 Self::White => 37,
41 Self::BrightBlack => 90,
42 Self::BrightRed => 91,
43 Self::BrightGreen => 92,
44 Self::BrightYellow => 93,
45 Self::BrightBlue => 94,
46 Self::BrightMagenta => 95,
47 Self::BrightCyan => 96,
48 Self::BrightWhite => 97,
49 }
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct Style {
55 fg_color: Option<Color>,
56}
57
58impl Default for Style {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64impl Style {
65 #[must_use]
66 pub const fn new() -> Self {
67 Self { fg_color: None }
68 }
69
70 #[must_use]
71 pub const fn fg(mut self, color: Color) -> Self {
72 self.fg_color = Some(color);
73 self
74 }
75}
76
77#[must_use]
78pub const fn style() -> Style {
79 Style::new()
80}
81
82#[must_use]
83pub const fn fg_black() -> Style {
84 style().fg(Color::Black)
85}
86#[must_use]
87pub const fn fg_red() -> Style {
88 style().fg(Color::Red)
89}
90#[must_use]
91pub const fn fg_green() -> Style {
92 style().fg(Color::Green)
93}
94#[must_use]
95pub const fn fg_yellow() -> Style {
96 style().fg(Color::Yellow)
97}
98#[must_use]
99pub const fn fg_blue() -> Style {
100 style().fg(Color::Blue)
101}
102#[must_use]
103pub const fn fg_magenta() -> Style {
104 style().fg(Color::Magenta)
105}
106#[must_use]
107pub const fn fg_cyan() -> Style {
108 style().fg(Color::Cyan)
109}
110#[must_use]
111pub const fn fg_white() -> Style {
112 style().fg(Color::White)
113}
114#[must_use]
115pub const fn fg_bright_black() -> Style {
116 style().fg(Color::BrightBlack)
117}
118#[must_use]
119pub const fn fg_bright_red() -> Style {
120 style().fg(Color::BrightRed)
121}
122#[must_use]
123pub const fn fg_bright_green() -> Style {
124 style().fg(Color::BrightGreen)
125}
126#[must_use]
127pub const fn fg_bright_yellow() -> Style {
128 style().fg(Color::BrightYellow)
129}
130#[must_use]
131pub const fn fg_bright_blue() -> Style {
132 style().fg(Color::BrightBlue)
133}
134#[must_use]
135pub const fn fg_bright_magenta() -> Style {
136 style().fg(Color::BrightMagenta)
137}
138#[must_use]
139pub const fn fg_bright_cyan() -> Style {
140 style().fg(Color::BrightCyan)
141}
142#[must_use]
143pub const fn fg_bright_white() -> Style {
144 style().fg(Color::BrightWhite)
145}
146
147#[allow(clippy::missing_errors_doc)]
148pub fn set_style(writer: &mut dyn Write, style: Style) -> io::Result<()> {
149 write!(writer, "\x1b[0m")?;
150 if let Some(color) = style.fg_color {
151 write!(writer, "\x1b[{}m", color.code())?;
152 }
153 Ok(())
154}
155#[allow(clippy::missing_errors_doc)]
156pub fn reset_style(writer: &mut dyn Write) -> io::Result<()> {
157 write!(writer, "\x1b[0m")
158}
159
160#[allow(clippy::missing_errors_doc)]
161pub fn print_styled<W: Write, T: Display>(
162 writer: &mut W,
163 style: Style,
164 value: T,
165) -> io::Result<()> {
166 set_style(writer, style)?;
167 write!(writer, "{value}")?;
168 reset_style(writer)?;
169 writer.flush()?;
170 Ok(())
171}
172
173#[allow(clippy::missing_errors_doc)]
174pub fn println_styled<W: Write, T: Display>(
175 writer: &mut W,
176 style: Style,
177 value: T,
178) -> io::Result<()> {
179 set_style(writer, style)?;
180 writeln!(writer, "{value}")?;
181 reset_style(writer)?;
182 writer.flush()?;
183 Ok(())
184}
185
186pub struct StyledText<T: Display> {
187 style: Style,
188 value: T,
189}
190
191impl<T: Display> StyledText<T> {
192 pub const fn new(style: Style, value: T) -> Self {
193 Self { style, value }
194 }
195}
196
197impl<T: Display> Display for StyledText<T> {
198 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
199 if let Some(color) = self.style.fg_color {
200 write!(f, "\x1b[{}m", color.code())?;
201 }
202 write!(f, "{}", self.value)?;
203 write!(f, "\x1b[0m")?;
204 Ok(())
205 }
206}
207
208pub const fn color<T: Display>(color: Color, value: T) -> StyledText<T> {
209 StyledText::new(style().fg(color), value)
210}
211
212pub const fn black<T: Display>(value: T) -> StyledText<T> {
213 color(Color::Black, value)
214}
215pub const fn red<T: Display>(value: T) -> StyledText<T> {
216 color(Color::Red, value)
217}
218pub const fn green<T: Display>(value: T) -> StyledText<T> {
219 color(Color::Green, value)
220}
221pub const fn yellow<T: Display>(value: T) -> StyledText<T> {
222 color(Color::Yellow, value)
223}
224pub const fn blue<T: Display>(value: T) -> StyledText<T> {
225 color(Color::Blue, value)
226}
227pub const fn magenta<T: Display>(value: T) -> StyledText<T> {
228 color(Color::Magenta, value)
229}
230pub const fn cyan<T: Display>(value: T) -> StyledText<T> {
231 color(Color::Cyan, value)
232}
233pub const fn white<T: Display>(value: T) -> StyledText<T> {
234 color(Color::White, value)
235}
236pub const fn bright_black<T: Display>(value: T) -> StyledText<T> {
237 color(Color::BrightBlack, value)
238}
239pub const fn bright_red<T: Display>(value: T) -> StyledText<T> {
240 color(Color::BrightRed, value)
241}
242pub const fn bright_green<T: Display>(value: T) -> StyledText<T> {
243 color(Color::BrightGreen, value)
244}
245pub const fn bright_yellow<T: Display>(value: T) -> StyledText<T> {
246 color(Color::BrightYellow, value)
247}
248pub const fn bright_blue<T: Display>(value: T) -> StyledText<T> {
249 color(Color::BrightBlue, value)
250}
251pub const fn bright_magenta<T: Display>(value: T) -> StyledText<T> {
252 color(Color::BrightMagenta, value)
253}
254pub const fn bright_cyan<T: Display>(value: T) -> StyledText<T> {
255 color(Color::BrightCyan, value)
256}
257pub const fn bright_white<T: Display>(value: T) -> StyledText<T> {
258 color(Color::BrightWhite, value)
259}
260
261#[derive(Debug, Clone)]
263pub struct Printer {
264 use_color: bool,
265}
266
267impl Default for Printer {
268 fn default() -> Self {
269 Self::new()
270 }
271}
272
273impl Printer {
274 #[must_use]
276 pub const fn new() -> Self {
277 Self { use_color: true }
278 }
279
280 #[must_use]
282 pub const fn without_color() -> Self {
283 Self { use_color: false }
284 }
285
286 #[must_use]
288 pub fn auto_detect() -> Self {
289 #[cfg(unix)]
290 {
291 use std::io::IsTerminal;
292 Self {
293 use_color: std::io::stdout().is_terminal(),
294 }
295 }
296 #[cfg(not(unix))]
297 {
298 Self { use_color: true }
299 }
300 }
301
302 pub fn enable_color(&mut self) {
304 self.use_color = true;
305 }
306
307 pub fn disable_color(&mut self) {
309 self.use_color = false;
310 }
311
312 #[must_use]
314 pub const fn colors_enabled(&self) -> bool {
315 self.use_color
316 }
317
318 pub fn print<W: Write, T: Display>(
320 &self,
321 writer: &mut W,
322 style: Style,
323 value: T,
324 ) -> io::Result<()> {
325 if self.use_color {
326 print_styled(writer, style, value)
327 } else {
328 write!(writer, "{value}")?;
329 writer.flush()
330 }
331 }
332
333 pub fn println<W: Write, T: Display>(
335 &self,
336 writer: &mut W,
337 style: Style,
338 value: T,
339 ) -> io::Result<()> {
340 if self.use_color {
341 println_styled(writer, style, value)
342 } else {
343 writeln!(writer, "{value}")?;
344 writer.flush()
345 }
346 }
347
348 #[must_use]
350 pub fn styled<T: Display>(&self, style: Style, value: T) -> PrinterStyledText<T> {
351 PrinterStyledText {
352 style,
353 value,
354 use_color: self.use_color,
355 }
356 }
357
358 #[must_use]
360 pub fn color<T: Display>(&self, color: Color, value: T) -> PrinterStyledText<T> {
361 self.styled(style().fg(color), value)
362 }
363}
364
365pub struct PrinterStyledText<T: Display> {
367 style: Style,
368 value: T,
369 use_color: bool,
370}
371
372impl<T: Display> Display for PrinterStyledText<T> {
373 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
374 if self.use_color {
375 if let Some(color) = self.style.fg_color {
376 write!(f, "\x1b[{}m", color.code())?;
377 }
378 write!(f, "{}", self.value)?;
379 write!(f, "\x1b[0m")?;
380 } else {
381 write!(f, "{}", self.value)?;
382 }
383 Ok(())
384 }
385}