prettylogger/
colors.rs

1//! Contains cosmetic, color-related utilities.
2
3/// Contains various color-related utilities for cosmetic customization.
4use std::{
5    collections::HashMap,
6    fmt::{
7        Display,
8        Formatter
9    }, sync::LazyLock,
10};
11
12use serde::{
13    Serialize,
14    Deserialize
15};
16
17/// Represents different colors. Used to color text or modify the appearance of
18/// log headers.
19///
20/// # Examples
21///
22/// Using a standard `Color` to customize log header appearance:
23/// ```rust
24/// # use prettylogger::{
25/// #     Logger,
26/// #     colors::Color,
27/// # };
28/// let mut logger = Logger::default();
29///
30/// logger.formatter.lock().unwrap().set_debug_color(Color::Gray);
31/// logger.formatter.lock().unwrap().set_info_color(Color::Green);
32/// logger.formatter.lock().unwrap().set_warning_color(Color::Yellow);
33/// logger.formatter.lock().unwrap().set_error_color(Color::Red);
34/// logger.formatter.lock().unwrap().set_fatal_color(Color::Magenta);
35/// ```
36///
37/// Using a custom `Color` to customize log header appearance:
38/// ```rust
39/// # use prettylogger::{
40/// #     Logger,
41/// #     colors::Color,
42/// # };
43/// let mut logger = Logger::default();
44///
45/// // Set a **bold white** color
46/// logger.formatter.lock().unwrap()
47///     .set_debug_color(Color::Custom(String::from("\x1b[97m")));
48/// ```
49#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default,
50    Serialize, Deserialize)]
51#[repr(i32)]
52pub enum Color
53{
54    None = 0,
55    Black = 1,
56    #[default]
57    Blue = 2,
58    Cyan = 3,
59    Green = 4,
60    Gray = 5,
61    Magenta = 6,
62    Red = 7,
63    White = 8,
64    Yellow = 9,
65
66    Custom(String) = 10,
67}
68
69const BLACK: &str = "\x1b[30m";
70const BLUE: &str = "\x1b[34m";
71const CYAN: &str = "\x1b[36m";
72const GREEN: &str = "\x1b[32m";
73const GRAY: &str = "\x1b[90m";
74const MAGENTA: &str = "\x1b[35m";
75const RED: &str = "\x1b[31m";
76const WHITE: &str = "\x1b[37m";
77const YELLOW: &str = "\x1b[33m";
78
79pub(crate) static RESET: &str = "\x1b[0m";
80
81static COLOR_MAP: LazyLock<HashMap<i32, &str>> = LazyLock::new(|| {
82    let mut m = HashMap::new();
83    m.insert(Color::None.into(), "");
84    m.insert(Color::Black.into(), BLACK);
85    m.insert(Color::Blue.into(), BLUE);
86    m.insert(Color::Cyan.into(), CYAN);
87    m.insert(Color::Green.into(), GREEN);
88    m.insert(Color::Gray.into(), GRAY);
89    m.insert(Color::Magenta.into(), MAGENTA);
90    m.insert(Color::Red.into(), RED);
91    m.insert(Color::White.into(), WHITE);
92    m.insert(Color::Yellow.into(), YELLOW);
93    m
94});
95
96/// Colors given text based on `color` value using ANSII escape codes.
97///
98/// # Examples
99///
100/// Using a `Color` enum to color text:
101/// ```
102/// # use prettylogger::colors::{Color, color_text};
103/// let colored_text = color_text("some text", Color::Red);
104/// # assert_eq!(colored_text, "\x1b[31msome text\x1b[0m");
105/// ```
106///
107/// Using a custom `Color` to color text:
108/// ```
109/// # use prettylogger::colors::{Color, color_text};
110/// let colored_text = color_text("some text",
111///     Color::Custom(String::from("\x1b[97m")));
112/// # assert_eq!(colored_text, "\x1b[97msome text\x1b[0m");
113/// ```
114pub fn color_text(text: &str, color: Color) -> String {
115    match color {
116        Color::Custom(s) => {
117            s + text + RESET
118        },
119        _ => {
120            if color != Color::None {
121                COLOR_MAP[&(color.into())].to_string() + text + RESET
122            }
123            else{
124                String::from(text)
125            }
126        }
127    }
128}
129
130impl Display for Color {
131    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
132        let level_str = match self {
133            Color::None => "None",
134            Color:: Black => "Black",
135            Color::Blue => "Blue",
136            Color::Cyan => "Cyan",
137            Color::Green => "Green",
138            Color::Gray => "Gray",
139            Color::Magenta => "Magenta",
140            Color::Red => "Red",
141            Color::White => "White",
142            Color::Yellow => "Yellow",
143
144            Color::Custom(str) => &format!("'{str}'")
145        };
146        write!(f, "{level_str}")
147    }
148}
149
150impl TryFrom<i32> for Color {
151    type Error = &'static str;
152    fn try_from(value: i32) -> Result<Self, Self::Error> {
153        match value {
154            0 => Ok(Color::None),
155            1 => Ok(Color::Black),
156            2 => Ok(Color::Blue),
157            3 => Ok(Color::Cyan),
158            4 => Ok(Color::Green),
159            5 => Ok(Color::Gray),
160            6 => Ok(Color::Magenta),
161            7 => Ok(Color::Red),
162            8 => Ok(Color::White),
163            9 => Ok(Color::Yellow),
164            18 => {Ok(Color::Custom(String::new()))}
165            _ => Err("Invalid value! Please provide a value in range 0-9."),
166        }
167    }
168}
169
170impl From<Color> for i32 {
171    fn from(color: Color) -> Self {
172        match color {
173            Color::None => 0,
174            Color::Black => 1,
175            Color::Blue => 2,
176            Color::Cyan => 3,
177            Color::Green => 4,
178            Color::Gray => 5,
179            Color::Magenta => 6,
180            Color::Red => 7,
181            Color::White => 8,
182            Color::Yellow => 9,
183            Color::Custom(_) => 10,
184        }
185    }
186}
187
188impl AsRef<str> for Color {
189    fn as_ref(&self) -> &str {
190        match self {
191            Color::None => "None",
192            Color:: Black => "Black",
193            Color::Blue => "Blue",
194            Color::Cyan => "Cyan",
195            Color::Green => "Green",
196            Color::Gray => "Gray",
197            Color::Magenta => "Magenta",
198            Color::Red => "Red",
199            Color::White => "White",
200            Color::Yellow => "Yellow",
201            Color::Custom(str) => str.as_str(),
202        }
203    }
204}