1use anyhow::Context;
2use console::{style, Color};
3use serde::{Deserialize, Serialize};
4
5use crate::traits::FeroxSerialize;
6use crate::utils::fmt_err;
7
8#[derive(Serialize, Deserialize, Default, Debug)]
9pub struct FeroxMessage {
11 #[serde(rename = "type")]
12 pub(crate) kind: String,
14
15 pub(crate) message: String,
17
18 pub(crate) level: String,
20
21 pub(crate) time_offset: f32,
23
24 pub(crate) module: String,
26}
27
28impl FeroxSerialize for FeroxMessage {
30 fn as_str(&self) -> String {
34 let (level_name, level_color) = match self.level.as_str() {
35 "ERROR" => ("ERR", Color::Red),
36 "WARN" => ("WRN", Color::Red),
37 "INFO" => ("INF", Color::Cyan),
38 "DEBUG" => ("DBG", Color::Yellow),
39 "TRACE" => ("TRC", Color::Magenta),
40 "WILDCARD" => ("WLD", Color::Cyan),
41 _ => ("MSG", Color::White),
42 };
43
44 format!(
45 "{} {:10.03} {} {}\n",
46 style(level_name).bg(level_color).black(),
47 style(self.time_offset).dim(),
48 self.module,
49 style(&self.message).dim(),
50 )
51 }
52
53 fn as_json(&self) -> anyhow::Result<String> {
65 let mut json = serde_json::to_string(&self).with_context(|| {
66 fmt_err(&format!(
67 "Could not convert {}:{} to JSON",
68 self.level, self.message
69 ))
70 })?;
71 json.push('\n');
72 Ok(json)
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn ferox_message_as_str_returns_string_with_newline() {
83 let message = FeroxMessage {
84 message: "message".to_string(),
85 module: "utils".to_string(),
86 time_offset: 1.0,
87 level: "INFO".to_string(),
88 kind: "log".to_string(),
89 };
90 let message_str = message.as_str();
91
92 assert!(message_str.contains("INF"));
93 assert!(message_str.contains("1.000"));
94 assert!(message_str.contains("utils"));
95 assert!(message_str.contains("message"));
96 assert!(message_str.ends_with('\n'));
97 }
98
99 #[test]
100 fn ferox_message_as_json_returns_json_representation_of_ferox_message_with_newline() {
102 let message = FeroxMessage {
103 message: "message".to_string(),
104 module: "utils".to_string(),
105 time_offset: 1.0,
106 level: "INFO".to_string(),
107 kind: "log".to_string(),
108 };
109
110 let message_str = message.as_json().unwrap();
111
112 let error_margin = f32::EPSILON;
113
114 let json: FeroxMessage = serde_json::from_str(&message_str).unwrap();
115 assert_eq!(json.module, message.module);
116 assert_eq!(json.message, message.message);
117 assert!((json.time_offset - message.time_offset).abs() < error_margin);
118 assert_eq!(json.level, message.level);
119 assert_eq!(json.kind, message.kind);
120 }
121
122 #[test]
123 fn message_defaults() {
125 let msg = FeroxMessage::default();
126 assert_eq!(msg.level, String::new());
127 assert_eq!(msg.kind, String::new());
128 assert_eq!(msg.message, String::new());
129 assert_eq!(msg.module, String::new());
130 assert!(msg.time_offset < 0.1);
131 }
132
133 #[test]
134 fn message_as_str_edges() {
136 let mut msg = FeroxMessage {
137 message: "message".to_string(),
138 module: "utils".to_string(),
139 time_offset: 1.0,
140 level: "WILDCARD".to_string(),
141 kind: "log".to_string(),
142 };
143 assert!(console::strip_ansi_codes(&msg.as_str()).starts_with("WLD"));
144
145 msg.level = "UNKNOWN".to_string();
146 assert!(console::strip_ansi_codes(&msg.as_str()).starts_with("MSG"));
147 }
148}