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