use std::borrow::Cow;
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EventMetadata {
pub level: String,
pub message: String,
pub target: String,
pub fields: BTreeMap<Cow<'static, str>, Cow<'static, str>>,
}
impl EventMetadata {
pub fn new(
level: String,
message: String,
target: String,
fields: BTreeMap<Cow<'static, str>, Cow<'static, str>>,
) -> Self {
Self {
level,
message,
target,
fields,
}
}
pub fn format_brief(&self) -> String {
format!("[{}] {}: {}", self.level, self.target, self.message)
}
pub fn format_detailed(&self) -> String {
if self.fields.is_empty() {
self.format_brief()
} else {
let fields_str: Vec<String> = self
.fields
.iter()
.map(|(k, v)| format!("{}=\"{}\"", k, v))
.collect();
format!("{} {{{}}}", self.format_brief(), fields_str.join(", "))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_brief() {
let metadata = EventMetadata::new(
"ERROR".to_string(),
"Connection failed".to_string(),
"database::connection".to_string(),
BTreeMap::new(),
);
assert_eq!(
metadata.format_brief(),
"[ERROR] database::connection: Connection failed"
);
}
#[test]
fn test_format_detailed_no_fields() {
let metadata = EventMetadata::new(
"INFO".to_string(),
"Request processed".to_string(),
"api::handler".to_string(),
BTreeMap::new(),
);
assert_eq!(
metadata.format_detailed(),
"[INFO] api::handler: Request processed"
);
}
#[test]
fn test_format_detailed_with_fields() {
let mut fields = BTreeMap::new();
fields.insert(Cow::Borrowed("error_code"), Cow::Borrowed("TIMEOUT"));
fields.insert(Cow::Borrowed("retry_count"), Cow::Borrowed("3"));
let metadata = EventMetadata::new(
"ERROR".to_string(),
"Connection failed".to_string(),
"database::connection".to_string(),
fields,
);
let formatted = metadata.format_detailed();
assert!(formatted.contains("[ERROR] database::connection: Connection failed"));
assert!(formatted.contains("error_code=\"TIMEOUT\""));
assert!(formatted.contains("retry_count=\"3\""));
}
}