mdlogger/
types.rs

1use serde::Serialize;
2use serde_json::{json, Map, Value};
3use std::str::FromStr;
4use std::fmt::Display;
5use time::format_description;
6use crate::{constants::{CRITICAL_ENABLED_KEY, CRITICAL_TEXT_KEY, DEBUG_ENABLED_KEY, DEBUG_TEXT_KEY, ENABLED_KEY, FATAL_ENABLED_KEY, FATAL_TEXT_KEY, INFO_ENABLED_KEY, INFO_TEXT_KEY, LOG_MESSAGE_FORMAT_KEY, PATTERN_KEY, TIMESTAMP_FORMAT_KEY, WARNING_ENABLED_KEY, WARNING_TEXT_KEY}, utils::{check_message_pattern, remove_quotes}};
7
8
9
10pub (crate) const PLAIN_TEXT_FORMAT: &str = "plain_text";
11pub (crate) const JSON_FORMAT: &str = "json";
12pub (crate) const JSON_PRETTY_FORMAT: &str = "json_pretty";
13
14
15pub (crate) const VALID_LOG_MESSAGE_FORMATS: [&str; 3] = [
16    PLAIN_TEXT_FORMAT,
17    JSON_FORMAT,
18    JSON_PRETTY_FORMAT
19];
20
21
22const CONFIGURABLE_KEYS: [&str; 13] = [
23    ENABLED_KEY,
24    TIMESTAMP_FORMAT_KEY,
25    DEBUG_ENABLED_KEY,
26    INFO_ENABLED_KEY,
27    WARNING_ENABLED_KEY,
28    CRITICAL_ENABLED_KEY,
29    DEBUG_TEXT_KEY,
30    INFO_TEXT_KEY,
31    WARNING_TEXT_KEY,
32    CRITICAL_TEXT_KEY,
33    FATAL_TEXT_KEY,
34    LOG_MESSAGE_FORMAT_KEY,
35    PATTERN_KEY,
36];
37
38/// Log message type enumeration
39#[derive(Serialize, Clone, Copy)]
40pub enum LogMsgType {
41    DebugMsgType,
42    InfoMsgType,
43    WarningMsgType,
44    CriticalMsgType,
45    FatalMsgType,
46}
47
48/// PartialEq trait implementation for log message type enumration
49impl PartialEq for LogMsgType {
50    fn eq(&self, other: &Self) -> bool {
51        *self as i32 == *other as i32
52    }
53}
54
55/// Maximum number of log mssage types
56pub const LOG_MSG_TYPE_NUM: usize = LogMsgType::FatalMsgType as usize + 1usize;
57
58// Log message format types enumeration
59pub (crate) enum LogMessageFormat {
60    PlainText,
61    Json,
62    JsonPretty
63}
64
65// Log message format error object
66pub (crate) struct LogMessageFormatErr {
67    message: String
68}
69
70// Display trait implementation for log message format error object
71impl Display for LogMessageFormatErr {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        write!(f, "{}", self.message)        
74    }
75}
76
77// FromStr trait implementation for log message format enumeration
78impl FromStr for LogMessageFormat {
79    type Err = LogMessageFormatErr;
80    
81    fn from_str(s: &str) -> Result<Self, Self::Err> {
82        let s_lowecase = s.to_lowercase();
83        if s_lowecase == PLAIN_TEXT_FORMAT {
84            Ok(LogMessageFormat::PlainText)
85        } else if s_lowecase == JSON_FORMAT {
86            Ok(LogMessageFormat::Json)
87        } else if s_lowecase == JSON_PRETTY_FORMAT {
88            Ok(LogMessageFormat::JsonPretty)
89        } else {
90            Err(LogMessageFormatErr {message: format!("<{}> is not a valid log message format, valid are ({})",
91                                    s_lowecase, VALID_LOG_MESSAGE_FORMATS.join(", "))})
92        }
93    }
94}
95
96/// Log handler base object
97pub struct LogHandlerBase {
98    name: String,
99    enabled: bool,
100    timestamp_format: String,
101    msg_types_enabled: [bool; LOG_MSG_TYPE_NUM],
102    msg_types_text: [String; LOG_MSG_TYPE_NUM],
103    message_format: String, 
104    pattern: String,
105    appname: String,
106    appver: String
107}
108
109/// Log handler base object implementation
110impl LogHandlerBase {
111    /// Create a new Log handler base object
112    /// see its use in ConsoleLogHandler::new function as an example
113    /// * `name` Log handler name
114    /// * `enabled` Log handler enabling flag
115    /// * `timestamp_format` log messag time stamo format
116    /// * `msg_types_enabled` log message type enabling flag
117    /// * `msg_types_text` log message type text
118    /// * `message_format` log message output text type (plain, json, json_pretty)
119    /// * `pattern` log message format pattern
120    /// * `appname` application name
121    /// * `appversion` application version
122    pub fn new (name: String, 
123                enabled: bool, 
124                timestamp_format: String,
125                msg_types_enabled: [bool; LOG_MSG_TYPE_NUM], 
126                msg_types_text: [String; LOG_MSG_TYPE_NUM],
127                message_format: String,
128                pattern: String, 
129                appname: String, 
130                appver: String) -> Self {
131        Self {
132            name,
133            enabled,
134            timestamp_format, 
135            msg_types_enabled,
136            msg_types_text,
137            message_format,
138            pattern,
139            appname,
140            appver    
141        }
142    }
143
144    /// Return log handler name
145    pub fn get_name(&self) ->&String {
146        &self.name
147    }
148
149    /// Return log handler enabling flag status
150    pub fn is_enabled(&self) ->bool {
151        self.enabled.clone()
152    }
153
154    /// Return log handler log message type enabling flag status
155    /// * `msg_type` log message type
156    pub fn is_msg_type_enabled(&self, msg_type: &LogMsgType) -> bool {
157        let idx = *msg_type as usize;
158        let mut result = false;
159        if idx < self.msg_types_enabled.len() {
160            result = self.msg_types_enabled[idx].clone();
161        } 
162
163        result
164    }
165
166    /// Return time stamp format
167    pub fn get_timestamp_format(&self) ->&String {
168        &self.timestamp_format
169    } 
170
171    /// Return log message format pattern
172    pub fn get_pattern(&self) ->&String {
173        &self.pattern
174    } 
175
176    /// Return application name
177    pub fn get_appname(&self) ->&String {
178        &self.appname
179    }
180
181    /// Return application version
182    pub fn get_appver(&self) ->&String {
183        &self.appver
184    }
185
186    /// Return log message type text
187    pub fn get_msg_types_text(&self) -> &[String; LOG_MSG_TYPE_NUM] {
188        &self.msg_types_text
189    }
190
191    /// Return log message format (plain, json, json_pretty)
192    pub fn get_message_format(&self) -> &String {
193        &self.message_format
194    }
195
196    /// Return if key argument is a log handler base configuration key
197    pub fn is_abaseconfig(&self, key: &str) -> bool {
198        CONFIGURABLE_KEYS.contains(&key)
199    }
200
201    /// Set log handler configuration key with a new value
202    /// * `key` configuration key name
203    /// * `value` new configuration key value
204    pub fn set_config(&mut self, key: &str, value: &Value) -> Result<Option<String>, String> {
205        let mut error = String::new();
206        if ENABLED_KEY == key {
207            match value.as_bool() {
208                Some(val) => {
209                    self.enabled = val;
210                },
211                None => {
212                    error = format!("{} needs a boolean value", key);
213                }
214            }
215        } else if TIMESTAMP_FORMAT_KEY == key {
216            match value.as_str() {
217                Some(val) => {
218                    let no_quotes_value = remove_quotes(val);
219                    if  let Err(parse_error) = format_description::parse(&no_quotes_value) {
220                        error = format!("{}", parse_error);
221                    } else {
222                        self.timestamp_format = no_quotes_value;                        
223                    }    
224                },
225                None => {
226                    error = format!("{} needs a string value", key);
227                }
228            }
229        } else if DEBUG_ENABLED_KEY == key {
230            match value.as_bool() {
231                Some(val) => {
232                    self.msg_types_enabled[LogMsgType::DebugMsgType as usize] = val;
233                },
234                None => {
235                    error = format!("{} needs a boolean value", key);
236                }
237            }
238        } else if INFO_ENABLED_KEY == key {
239            match value.as_bool() {
240                Some(val) => {
241                    self.msg_types_enabled[LogMsgType::InfoMsgType as usize] = val;
242                },
243                None => {
244                    error = format!("{} needs a boolean value", key);
245                }
246            }
247        } else if WARNING_ENABLED_KEY == key {
248            match value.as_bool() {
249                Some(val) => {
250                    self.msg_types_enabled[LogMsgType::WarningMsgType as usize] = val;
251                },
252                None => {
253                    error = format!("{} needs a boolean value", key);
254                }
255            }
256        } else if CRITICAL_ENABLED_KEY == key {
257            match value.as_bool() {
258                Some(val) => {
259                    self.msg_types_enabled[LogMsgType::CriticalMsgType as usize] = val;
260                },
261                None => {
262                    error = format!("{} needs a boolean value", key);
263                }
264            }
265        } else if DEBUG_TEXT_KEY == key {
266            match value.as_str() {
267                Some(val) => {
268                    self.msg_types_text[LogMsgType::DebugMsgType as usize] = String::from(val);
269                },
270                None => {
271                    error = format!("{} needs a string value", key);
272                }
273            }
274        } else if INFO_TEXT_KEY == key{
275            match value.as_str() {
276                Some(val) => {
277                    self.msg_types_text[LogMsgType::InfoMsgType as usize] = String::from(val);
278                },
279                None => {
280                    error = format!("{} needs a string value", key);
281                }
282            }
283        } else if WARNING_TEXT_KEY == key{
284            match value.as_str() {
285                Some(val) => {
286                    self.msg_types_text[LogMsgType::WarningMsgType as usize] = String::from(val);
287                },
288                None => {
289                    error = format!("{} needs a string value", key);
290                }
291            }        
292        } else if CRITICAL_TEXT_KEY == key{
293            match value.as_str() {
294                Some(val) => {
295                    self.msg_types_text[LogMsgType::CriticalMsgType as usize] = String::from(val);
296                },
297                None => {
298                    error = format!("{} needs a string value", key);
299                }
300            }
301        } else if FATAL_TEXT_KEY == key {
302            match value.as_str() {
303                Some(val) => {
304                    self.msg_types_text[LogMsgType::FatalMsgType as usize] = String::from(val);
305                },
306                None => {
307                    error = format!("{} needs a string value", key);
308                }
309            }
310        } else if LOG_MESSAGE_FORMAT_KEY == key {
311            match value.as_str() {
312                Some(val) => {
313                    match val.parse::<LogMessageFormat>() {
314                        Ok(_) => {
315                            self.message_format = String::from(val);
316                        },
317                        Err(parse_error) => {
318                            error = format!("{}", parse_error);
319                        }
320                    }
321                },
322                None => {
323                    error = format!("{} needs a string value", key);
324                }
325            }
326        } else if PATTERN_KEY == key {
327            match value.as_str() {
328                Some(val) => {
329                    if let Err(check_error) = check_message_pattern(&val.to_string()) {
330                        error = check_error;
331                    } else {
332                        self.pattern = String::from(val);
333                    }
334                },
335                None => {
336                    error = format!("{} needs a string value", key);
337                }
338            }
339        
340        } else {
341            error = format!("{} is not a valid configuration key", key);
342        }
343
344        if error.len() > 0 {
345            Err(error)
346        } else {
347            Ok(None)
348        }
349    } 
350
351    /// Return log handler base object configurationas a Map json value
352    pub fn get_config(&self) -> Map<String, Value> {
353        let mut config = Map::new();
354        config.insert(String::from("name"), json!(self.name));
355        config.insert(String::from(ENABLED_KEY), json!(self.enabled));
356        config.insert(String::from(TIMESTAMP_FORMAT_KEY), json!(format!("\"{}\"", self.timestamp_format)));
357        config.insert(String::from(DEBUG_ENABLED_KEY), json!(self.msg_types_enabled[LogMsgType::DebugMsgType as usize]));
358        config.insert(String::from(INFO_ENABLED_KEY), json!(self.msg_types_enabled[LogMsgType::InfoMsgType as usize]));
359        config.insert(String::from(WARNING_ENABLED_KEY), json!(self.msg_types_enabled[LogMsgType::WarningMsgType as usize]));
360        config.insert(String::from(CRITICAL_ENABLED_KEY), json!(self.msg_types_enabled[LogMsgType::CriticalMsgType as usize]));
361        config.insert(String::from(FATAL_ENABLED_KEY), json!(self.msg_types_enabled[LogMsgType::FatalMsgType as usize]));
362
363        config.insert(String::from(DEBUG_TEXT_KEY), json!(self.msg_types_text[LogMsgType::DebugMsgType as usize]));
364        config.insert(String::from(INFO_TEXT_KEY), json!(self.msg_types_text[LogMsgType::InfoMsgType as usize]));
365        config.insert(String::from(WARNING_TEXT_KEY), json!(self.msg_types_text[LogMsgType::WarningMsgType as usize]));
366        config.insert(String::from(CRITICAL_TEXT_KEY), json!(self.msg_types_text[LogMsgType::CriticalMsgType as usize]));
367        config.insert(String::from(FATAL_TEXT_KEY), json!(self.msg_types_text[LogMsgType::FatalMsgType as usize]));
368        
369        config.insert(String::from(PATTERN_KEY), json!(self.pattern));
370        config.insert(String::from(LOG_MESSAGE_FORMAT_KEY), json!(self.get_message_format()));
371        config.insert(String::from("appname"), json!(self.appname));
372        config.insert(String::from("appver"), json!(self.appver));
373
374        config
375    }
376}