1use std::{
3 io::{self, Cursor, Read, Write},
4 sync::{Arc, Mutex, MutexGuard},
5};
6use tracing_subscriber::fmt::MakeWriter;
7
8#[derive(Debug, Default, Clone)]
11pub struct LogBuffer {
12 inner: Arc<Mutex<Cursor<Vec<u8>>>>,
13}
14
15impl LogBuffer {
16 pub fn content(&self) -> String {
18 let mut guard = self.inner.lock().expect("lock poisoned");
19 let pos = guard.position();
20 guard.set_position(0);
21 let mut s = String::new();
22 _ = guard.read_to_string(&mut s);
23 guard.set_position(pos);
24
25 s
26 }
27
28 pub fn clear(&self) {
30 let mut guard = self.inner.lock().expect("lock poisoned");
31 *guard = Default::default();
32 }
33}
34
35#[derive(Debug)]
37pub struct LogWriter<'a>(MutexGuard<'a, Cursor<Vec<u8>>>);
38
39impl Write for LogWriter<'_> {
40 #[inline]
41 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
42 self.0.write(buf)
43 }
44
45 #[inline]
46 fn flush(&mut self) -> io::Result<()> {
47 self.0.flush()
48 }
49
50 #[inline]
51 fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
52 self.0.write_vectored(bufs)
53 }
54
55 #[inline]
56 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
57 self.0.write_all(buf)
58 }
59
60 #[inline]
61 fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> {
62 self.0.write_fmt(fmt)
63 }
64}
65
66impl<'a> MakeWriter<'a> for LogBuffer {
67 type Writer = LogWriter<'a>;
68
69 fn make_writer(&'a self) -> Self::Writer {
70 LogWriter(self.inner.lock().expect("lock poisoned"))
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn reading_log_content_works() {
80 let logs = LogBuffer::default();
81 _ = logs.make_writer().write(b"hello, world!\n").unwrap();
82
83 let content = logs.content();
84 assert_eq!(content, "hello, world!\n");
85
86 _ = logs.make_writer().write(b"a second line\n").unwrap();
87
88 let content = logs.content();
89 assert_eq!(content, "hello, world!\na second line\n");
90 }
91
92 #[test]
93 fn clearing_works() {
94 let logs = LogBuffer::default();
95 _ = logs.make_writer().write(b"hello, world!\n").unwrap();
96
97 let content = logs.content();
98 assert_eq!(content, "hello, world!\n");
99
100 logs.clear();
101
102 let content = logs.content();
103 assert!(content.is_empty());
104 }
105}