#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct LogEntry {
pub level: LogLevel,
pub message: String,
pub timestamp: f64,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SinkType {
Console,
Buffer,
Null,
}
#[allow(dead_code)]
pub struct LogSink {
pub sink_type: SinkType,
pub min_level: LogLevel,
entries: Vec<LogEntry>,
}
#[allow(dead_code)]
pub fn new_log_sink(sink_type: SinkType) -> LogSink {
LogSink {
sink_type,
min_level: LogLevel::Trace,
entries: Vec::new(),
}
}
#[allow(dead_code)]
pub fn log_sink_write(sink: &mut LogSink, level: LogLevel, message: &str, timestamp: f64) {
if sink.sink_type == SinkType::Null {
return;
}
if level < sink.min_level {
return;
}
sink.entries.push(LogEntry {
level,
message: message.to_string(),
timestamp,
});
}
#[allow(dead_code)]
pub fn log_sink_entry_count(sink: &LogSink) -> usize {
sink.entries.len()
}
#[allow(dead_code)]
pub fn log_sink_entries(sink: &LogSink) -> &[LogEntry] {
&sink.entries
}
#[allow(dead_code)]
pub fn log_sink_clear(sink: &mut LogSink) {
sink.entries.clear();
}
#[allow(dead_code)]
pub fn log_level_value(level: LogLevel) -> u8 {
match level {
LogLevel::Trace => 0,
LogLevel::Debug => 1,
LogLevel::Info => 2,
LogLevel::Warn => 3,
LogLevel::Error => 4,
}
}
#[allow(dead_code)]
pub fn log_level_name(level: LogLevel) -> &'static str {
match level {
LogLevel::Trace => "TRACE",
LogLevel::Debug => "DEBUG",
LogLevel::Info => "INFO",
LogLevel::Warn => "WARN",
LogLevel::Error => "ERROR",
}
}
#[allow(dead_code)]
pub fn sink_type_name(t: SinkType) -> &'static str {
match t {
SinkType::Console => "console",
SinkType::Buffer => "buffer",
SinkType::Null => "null",
}
}
#[allow(dead_code)]
pub fn set_log_min_level(sink: &mut LogSink, min_level: LogLevel) {
sink.min_level = min_level;
}
#[allow(dead_code)]
pub fn log_sink_filter_by_level(sink: &LogSink, level: LogLevel) -> Vec<&LogEntry> {
sink.entries.iter().filter(|e| e.level == level).collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn make_buffer_sink() -> LogSink {
new_log_sink(SinkType::Buffer)
}
#[test]
fn test_new_sink_empty() {
let sink = make_buffer_sink();
assert_eq!(log_sink_entry_count(&sink), 0);
assert!(log_sink_entries(&sink).is_empty());
}
#[test]
fn test_write_and_count() {
let mut sink = make_buffer_sink();
log_sink_write(&mut sink, LogLevel::Info, "hello", 0.0);
log_sink_write(&mut sink, LogLevel::Warn, "careful", 1.0);
assert_eq!(log_sink_entry_count(&sink), 2);
}
#[test]
fn test_null_sink_discards_all() {
let mut sink = new_log_sink(SinkType::Null);
log_sink_write(&mut sink, LogLevel::Error, "ignored", 0.0);
assert_eq!(log_sink_entry_count(&sink), 0);
}
#[test]
fn test_min_level_filter() {
let mut sink = make_buffer_sink();
set_log_min_level(&mut sink, LogLevel::Warn);
log_sink_write(&mut sink, LogLevel::Trace, "too low", 0.0);
log_sink_write(&mut sink, LogLevel::Info, "still low", 1.0);
log_sink_write(&mut sink, LogLevel::Warn, "accepted", 2.0);
log_sink_write(&mut sink, LogLevel::Error, "accepted too", 3.0);
assert_eq!(log_sink_entry_count(&sink), 2);
}
#[test]
fn test_log_sink_clear() {
let mut sink = make_buffer_sink();
log_sink_write(&mut sink, LogLevel::Debug, "msg", 0.0);
log_sink_clear(&mut sink);
assert_eq!(log_sink_entry_count(&sink), 0);
}
#[test]
fn test_log_level_values_ordered() {
assert!(log_level_value(LogLevel::Trace) < log_level_value(LogLevel::Debug));
assert!(log_level_value(LogLevel::Debug) < log_level_value(LogLevel::Info));
assert!(log_level_value(LogLevel::Info) < log_level_value(LogLevel::Warn));
assert!(log_level_value(LogLevel::Warn) < log_level_value(LogLevel::Error));
}
#[test]
fn test_log_level_names() {
assert_eq!(log_level_name(LogLevel::Trace), "TRACE");
assert_eq!(log_level_name(LogLevel::Error), "ERROR");
}
#[test]
fn test_sink_type_names() {
assert_eq!(sink_type_name(SinkType::Console), "console");
assert_eq!(sink_type_name(SinkType::Buffer), "buffer");
assert_eq!(sink_type_name(SinkType::Null), "null");
}
#[test]
fn test_filter_by_level() {
let mut sink = make_buffer_sink();
log_sink_write(&mut sink, LogLevel::Info, "a", 0.0);
log_sink_write(&mut sink, LogLevel::Warn, "b", 1.0);
log_sink_write(&mut sink, LogLevel::Info, "c", 2.0);
let infos = log_sink_filter_by_level(&sink, LogLevel::Info);
assert_eq!(infos.len(), 2);
let warns = log_sink_filter_by_level(&sink, LogLevel::Warn);
assert_eq!(warns.len(), 1);
}
#[test]
fn test_entries_slice_content() {
let mut sink = make_buffer_sink();
log_sink_write(&mut sink, LogLevel::Error, "boom", 42.0);
let entries = log_sink_entries(&sink);
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].message, "boom");
assert!((entries[0].timestamp - 42.0).abs() < 1e-9);
}
}