1use crate::formatter::Formatter;
2use crate::types::Record;
3use std::io::Write;
4use std::sync::Mutex;
5
6pub trait Handler: Send + Sync {
12 fn handle(&self, record: &Record);
14 fn formatter(&self) -> &Formatter;
16}
17
18pub struct StreamHandler<W: Write + Send> {
21 formatter: Formatter,
22 stream: Mutex<W>,
23}
24
25impl<W: Write + Send> StreamHandler<W> {
26 pub fn new(formatter: Formatter, stream: W) -> Self {
28 Self {
29 formatter,
30 stream: Mutex::new(stream),
31 }
32 }
33 pub fn with_pattern(stream: W, pattern: &str) -> Self {
35 Self::new(Formatter::new(pattern), stream)
36 }
37}
38
39impl<W: Write + Send> Handler for StreamHandler<W> {
40 fn handle(&self, record: &Record) {
41 let message = self.formatter.format(record);
42 let mut stream = self
43 .stream
44 .lock()
45 .unwrap_or_else(|poisoned| poisoned.into_inner());
46 if let Err(error) = stream.write_all(message.as_bytes()) {
47 eprintln!("Error writing to stream: {error}");
48 }
49 }
50 fn formatter(&self) -> &Formatter {
51 &self.formatter
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use std::collections::HashMap;
59 use std::sync::Arc;
60
61 #[derive(Clone)]
63 struct SharedBuf(Arc<Mutex<Vec<u8>>>);
64
65 impl Write for SharedBuf {
66 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
67 self.0.lock().unwrap().extend_from_slice(buf);
68 Ok(buf.len())
69 }
70 fn flush(&mut self) -> std::io::Result<()> {
71 Ok(())
72 }
73 }
74
75 #[test]
76 fn stream_handler_writes_formatted_record() {
77 let buf = Arc::new(Mutex::new(Vec::new()));
78 let handler = StreamHandler::with_pattern(SharedBuf(buf.clone()), "%(level): %(message)");
79
80 let record = HashMap::from([("level", "INFO"), ("message", "hello")]);
81 handler.handle(&record);
82
83 let written = String::from_utf8(buf.lock().unwrap().clone()).unwrap();
84 assert_eq!(written, "INFO: hello\n");
85 }
86
87 #[test]
88 fn formatter_accessor_returns_the_handlers_formatter() {
89 let handler = StreamHandler::with_pattern(Vec::<u8>::new(), "%(message)");
90 let record = HashMap::from([("message", "x")]);
92 assert_eq!(handler.formatter().format(&record), "x\n");
93 }
94}