prettylogger/
format.rs

1//! Contains `LogFormatter`, used to create formatted log messages from raw log
2//! structs.
3
4/// Contains `LogFormatter`, used to create formatted log messages from raw log
5/// structs.
6use serde::{Serialize, Deserialize};
7use chrono::{Local, DateTime};
8
9use crate::{
10    LogType, Error,
11    colors::{Color, color_text},
12    config::LogStruct,
13};
14
15/// The `LogFormatter` is used for turning raw log structs into log messages
16/// based on its configuration.
17///
18/// # Examples
19///
20/// Using a `LogFormatter` to print a log:
21/// ```
22/// # use prettylogger::{
23/// #    config::LogStruct,
24/// #    format::LogFormatter,
25/// # };
26/// // Create a `LogFormatter` with default configuration
27/// let mut formatter = LogFormatter::default();
28///
29/// // Set a log format
30/// formatter.set_log_format("[ %h %m ]");
31///
32/// // Obtain a formatted log from a `LogStruct`
33/// let log = formatter.format_log(&LogStruct::debug("Hello from LogStruct!"));
34///
35/// // Print the formatted log message
36/// print!("{}", &log);
37/// ```
38#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize,
39    Deserialize)]
40pub struct LogFormatter {
41    pub(crate) log_header_color_enabled: bool,
42
43    pub(crate) debug_color: Color,
44    pub(crate) info_color: Color,
45    pub(crate) warning_color: Color,
46    pub(crate) error_color: Color,
47    pub(crate) fatal_color: Color,
48
49    pub(crate) debug_header: String,
50    pub(crate) info_header: String,
51    pub(crate) warning_header: String,
52    pub(crate) error_header: String,
53    pub(crate) fatal_header: String,
54
55    pub(crate) log_format: String,
56    pub(crate) datetime_format: String,
57
58    #[serde(skip)]
59    pub(crate) show_datetime: Option<bool>,
60}
61
62impl LogFormatter {
63    pub(crate) fn get_datetime_formatted(&mut self, datetime: &DateTime<Local>) -> String {
64        match self.show_datetime {
65            Some(b) => {
66                match b {
67                    true => {
68                        return datetime.format(&self.datetime_format)
69                            .to_string();
70                    },
71                    false => {
72                        return String::new();
73                    },
74                }
75
76            },
77            None => {
78                match self.log_format.contains("%d") {
79                    true => {
80                        self.show_datetime = Some(true);
81                        return datetime.format(&self.datetime_format)
82                            .to_string();
83                    },
84                    false => {
85                        self.show_datetime = Some(false);
86                        return String::new();
87                    },
88                }
89            }
90        }
91    }
92
93    pub(crate) fn log_header_color(&self, log_type: LogType) -> Color {
94        match log_type {
95            LogType::Debug => self.debug_color.clone(),
96            LogType::Info => self.info_color.clone(),
97            LogType::Warning => self.warning_color.clone(),
98            LogType::Err => self.error_color.clone(),
99            LogType::FatalError => self.fatal_color.clone(),
100        }
101    }
102
103    pub(crate) fn colorify(&self, text: &str, color: Color) -> String {
104        if self.log_header_color_enabled {
105            return color_text(text, color);
106        }
107        return text.to_string()
108    }
109
110    pub(crate) fn get_log_type_header(&self, log_type: LogType) -> String {
111        match log_type {
112            LogType::Debug => {
113                return self.colorify(&self.debug_header,
114                    self.log_header_color(log_type))
115            }
116            LogType::Info => {
117                return self.colorify(&self.info_header,
118                    self.log_header_color(log_type))
119            }
120            LogType::Warning => {
121                return self.colorify(&self.warning_header,
122                    self.log_header_color(log_type))
123            }
124            LogType::Err => {
125                return self.colorify(&self.error_header,
126                    self.log_header_color(log_type))
127            }
128            LogType::FatalError => {
129                return self.colorify(&self.fatal_header,
130                    self.log_header_color(log_type))
131            }
132        }
133    }
134
135    pub(crate) fn get_log_headers(&mut self, log: &LogStruct)
136    -> (String, String) {
137        let header = self.get_log_type_header(log.log_type);
138        let datetime = self.get_datetime_formatted(&log.datetime);
139        return (header, datetime);
140    }
141
142    /// Returns a log entry from a `LogStruct` based on current `LogFormatter`
143    /// configuration.
144    ///
145    /// # Examples
146    /// ```
147    /// # use prettylogger::{format::LogFormatter, config::LogStruct};
148    /// let mut formatter = LogFormatter::default();
149    /// let log_string = formatter.format_log(&LogStruct::error("Error!"));
150    /// ```
151    pub fn format_log(&mut self, log: &LogStruct) -> String {
152        let headers = self.get_log_headers(log);
153        let mut result = String::new();
154        let mut char_iter = self
155            .log_format.char_indices().peekable();
156
157        while let Some((_, c)) = char_iter.next() {
158            match c {
159                '%' => {
160                    if let Some((_, nc)) = char_iter.peek() {
161                        match nc {
162                            'h' => result += &headers.0,
163                            'd' => result += &headers.1,
164                            'm' => result += &log.message,
165                            _ => result += &nc.to_string(),
166                        }
167                        char_iter.next();
168                    }
169                }
170                _ => {
171                    result += &c.to_string();
172                }
173            }
174        }
175
176        result += "\n";
177        return result
178    }
179
180    /// Toggles colored log headers.
181    /// * `true`: Log headers will have colors
182    /// * `false`: No colors :(
183    pub fn toggle_log_header_color<I: Into<bool>>(&mut self, enabled: I) {
184        self.log_header_color_enabled = enabled.into();
185    }
186
187    /// Sets **debug log header** color.
188    pub fn set_debug_color<I: Into<Color>>(&mut self, color: I) {
189        self.debug_color = color.into();
190    }
191
192    /// Sets **info log header** color.
193    pub fn set_info_color<I: Into<Color>>(&mut self, color: I) {
194        self.info_color = color.into();
195    }
196
197    /// Sets **warning header** color.
198    pub fn set_warning_color<I: Into<Color>>(&mut self, color: I) {
199        self. warning_color = color.into();
200    }
201
202    /// Sets **error header** color.
203    pub fn set_error_color<I: Into<Color>>(&mut self, color: I) {
204        self.error_color = color.into();
205    }
206
207    /// Sets **fatal error header** color.
208    pub fn set_fatal_color<I: Into<Color>>(&mut self, color: I) {
209        self.fatal_color = color.into();
210    }
211
212    /// Sets **debug log header** format.
213    pub fn set_debug_header(&mut self, header: &str) {
214        self.debug_header = header.to_string();
215    }
216
217    /// Sets **info log header** format.
218    pub fn set_info_header(&mut self, header: &str) {
219        self.info_header = header.to_string();
220    }
221
222    /// Sets **warning header** format.
223    pub fn set_warning_header(&mut self, header: &str) {
224        self.warning_header = header.to_string();
225    }
226
227    /// Sets **error header** format.
228    pub fn set_error_header(&mut self, header: &str) {
229        self.error_header = header.to_string();
230    }
231
232    /// Sets **fatal error header** format.
233    pub fn set_fatal_header(&mut self, header: &str) {
234        self.fatal_header = header.to_string();
235    }
236
237    /// Sets datetime format.
238    pub fn set_datetime_format(&mut self, format: &str) {
239        self.datetime_format = String::from(format);
240        self.show_datetime = None;
241    }
242
243    /// Sets the log format.
244    ///
245    /// There are several placeholders in a log format string:
246    /// * `%d`: The timestamp.
247    /// * `%h`: The header indicating the log type (e.g., debug, error, etc.)
248    /// * `%m`: The log message (this placeholder is mandatory, you will
249    ///   get an error if you don't include it in your log format).
250    ///
251    /// You can have multiple placeholders of the same type in a format string.
252    ///
253    /// # Examples
254    /// ```
255    /// # use prettylogger::{
256    /// #     format::LogFormatter,
257    /// #     config::LogStruct,
258    /// # };
259    /// let mut formatter = LogFormatter::default();
260    ///
261    /// // Do a nice XML-like format
262    /// formatter.set_log_format("<l> <h>%h</h> <m>%m</m> </l>");
263    /// print!("{}", formatter.format_log(&LogStruct::debug("Hello, World!")));
264    /// ```
265    ///
266    /// Returns an error when the `%m` placeholder is missing.
267    pub fn set_log_format(&mut self, format: &str) -> Result<(), Error> {
268        if format.contains("%m") {
269            self.log_format = String::from(format);
270            Ok(())
271        }
272        else {
273            Err(Error::new("Expected a message placeholder!"))
274        }
275    }
276}
277
278impl Default for LogFormatter {
279    fn default() -> LogFormatter {
280        let log_format = String::from("[%h] %m");
281        LogFormatter {
282            log_header_color_enabled: true,
283
284            debug_color: Color::Blue,
285            info_color: Color::Green,
286            warning_color: Color::Yellow,
287            error_color: Color::Red,
288            fatal_color: Color::Magenta,
289
290            debug_header: String::from("DBG"),
291            info_header: String::from("INF"),
292            warning_header: String::from("WAR"),
293            error_header: String::from("ERR"),
294            fatal_header: String::from("FATAL"),
295
296            log_format: log_format.clone(),
297            datetime_format: String::from("%Y-%m-%d %H:%M:%S"),
298
299            show_datetime: None,
300        }
301    }
302}