use anyhow::Context;
use console::{style, Color};
use serde::{Deserialize, Serialize};
use crate::traits::FeroxSerialize;
use crate::utils::fmt_err;
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct FeroxMessage {
#[serde(rename = "type")]
pub(crate) kind: String,
pub(crate) message: String,
pub(crate) level: String,
pub(crate) time_offset: f32,
pub(crate) module: String,
}
impl FeroxSerialize for FeroxMessage {
fn as_str(&self) -> String {
let (level_name, level_color) = match self.level.as_str() {
"ERROR" => ("ERR", Color::Red),
"WARN" => ("WRN", Color::Red),
"INFO" => ("INF", Color::Cyan),
"DEBUG" => ("DBG", Color::Yellow),
"TRACE" => ("TRC", Color::Magenta),
"WILDCARD" => ("WLD", Color::Cyan),
_ => ("MSG", Color::White),
};
format!(
"{} {:10.03} {} {}\n",
style(level_name).bg(level_color).black(),
style(self.time_offset).dim(),
self.module,
style(&self.message).dim(),
)
}
fn as_json(&self) -> anyhow::Result<String> {
let mut json = serde_json::to_string(&self).with_context(|| {
fmt_err(&format!(
"Could not convert {}:{} to JSON",
self.level, self.message
))
})?;
json.push('\n');
Ok(json)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ferox_message_as_str_returns_string_with_newline() {
let message = FeroxMessage {
message: "message".to_string(),
module: "utils".to_string(),
time_offset: 1.0,
level: "INFO".to_string(),
kind: "log".to_string(),
};
let message_str = message.as_str();
assert!(message_str.contains("INF"));
assert!(message_str.contains("1.000"));
assert!(message_str.contains("utils"));
assert!(message_str.contains("message"));
assert!(message_str.ends_with('\n'));
}
#[test]
fn ferox_message_as_json_returns_json_representation_of_ferox_message_with_newline() {
let message = FeroxMessage {
message: "message".to_string(),
module: "utils".to_string(),
time_offset: 1.0,
level: "INFO".to_string(),
kind: "log".to_string(),
};
let message_str = message.as_json().unwrap();
let error_margin = f32::EPSILON;
let json: FeroxMessage = serde_json::from_str(&message_str).unwrap();
assert_eq!(json.module, message.module);
assert_eq!(json.message, message.message);
assert!((json.time_offset - message.time_offset).abs() < error_margin);
assert_eq!(json.level, message.level);
assert_eq!(json.kind, message.kind);
}
#[test]
fn message_defaults() {
let msg = FeroxMessage::default();
assert_eq!(msg.level, String::new());
assert_eq!(msg.kind, String::new());
assert_eq!(msg.message, String::new());
assert_eq!(msg.module, String::new());
assert!(msg.time_offset < 0.1);
}
#[test]
fn message_as_str_edges() {
let mut msg = FeroxMessage {
message: "message".to_string(),
module: "utils".to_string(),
time_offset: 1.0,
level: "WILDCARD".to_string(),
kind: "log".to_string(),
};
assert!(console::strip_ansi_codes(&msg.as_str()).starts_with("WLD"));
msg.level = "UNKNOWN".to_string();
assert!(console::strip_ansi_codes(&msg.as_str()).starts_with("MSG"));
}
}