use crate::formatter::Formatter;
use crate::types::Record;
use std::io::Write;
use std::sync::Mutex;
pub trait Handler: Send + Sync {
fn handle(&self, record: &Record);
fn formatter(&self) -> &Formatter;
}
pub struct StreamHandler<W: Write + Send> {
formatter: Formatter,
stream: Mutex<W>,
}
impl<W: Write + Send> StreamHandler<W> {
pub fn new(formatter: Formatter, stream: W) -> Self {
Self {
formatter,
stream: Mutex::new(stream),
}
}
pub fn with_pattern(stream: W, pattern: &str) -> Self {
Self::new(Formatter::new(pattern), stream)
}
}
impl<W: Write + Send> Handler for StreamHandler<W> {
fn handle(&self, record: &Record) {
let message = self.formatter.format(record);
let mut stream = self
.stream
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
if let Err(error) = stream.write_all(message.as_bytes()) {
eprintln!("Error writing to stream: {error}");
}
}
fn formatter(&self) -> &Formatter {
&self.formatter
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Clone)]
struct SharedBuf(Arc<Mutex<Vec<u8>>>);
impl Write for SharedBuf {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0.lock().unwrap().extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[test]
fn stream_handler_writes_formatted_record() {
let buf = Arc::new(Mutex::new(Vec::new()));
let handler = StreamHandler::with_pattern(SharedBuf(buf.clone()), "%(level): %(message)");
let record = HashMap::from([("level", "INFO"), ("message", "hello")]);
handler.handle(&record);
let written = String::from_utf8(buf.lock().unwrap().clone()).unwrap();
assert_eq!(written, "INFO: hello\n");
}
#[test]
fn formatter_accessor_returns_the_handlers_formatter() {
let handler = StreamHandler::with_pattern(Vec::<u8>::new(), "%(message)");
let record = HashMap::from([("message", "x")]);
assert_eq!(handler.formatter().format(&record), "x\n");
}
}