ad_editor/
log.rs

1//! Handling of our internal logs so they can be viewed in the editor itself
2use std::{
3    io::{self, Cursor, Read, Write},
4    sync::{Arc, Mutex, MutexGuard},
5};
6use tracing_subscriber::fmt::MakeWriter;
7
8/// A central log writer wrapping an in memory buffer so we can display logs within the editor
9/// itself.
10#[derive(Debug, Default, Clone)]
11pub struct LogBuffer {
12    inner: Arc<Mutex<Cursor<Vec<u8>>>>,
13}
14
15impl LogBuffer {
16    /// Return the full content of the log
17    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    /// Clear the contents of the current log
29    pub fn clear(&self) {
30        let mut guard = self.inner.lock().expect("lock poisoned");
31        *guard = Default::default();
32    }
33}
34
35/// A handle implementing [Write] that can be used by tracing for writing our logs
36#[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}