miden_diagnostics/
emitter.rs

1use parking_lot::Mutex;
2
3use crate::term::termcolor::*;
4
5/// The [Emitter] trait is used for controlling how diagnostics are displayed.
6///
7/// An [Emitter] must produce a [Buffer] for use by the rendering
8/// internals, and its own print implementation.
9///
10/// When a diagnostic is being emitted, a new [Buffer] is allocated,
11/// the diagnostic is rendered into it, and then the buffer is passed
12/// to `print` for display by the [Emitter] implementation.
13pub trait Emitter: Send + Sync {
14    /// Construct a new [Buffer] for use by the renderer
15    fn buffer(&self) -> Buffer;
16    /// Display the contents of the given [Buffer]
17    fn print(&self, buffer: Buffer) -> std::io::Result<()>;
18}
19
20/// [DefaultEmitter] is used for rendering to stderr, and as is implied
21/// by the name, is the default emitter implementation.
22pub struct DefaultEmitter {
23    writer: BufferWriter,
24}
25impl DefaultEmitter {
26    /// Construct a new [DefaultEmitter] with the given [ColorChoice] behavior.
27    pub fn new(color: ColorChoice) -> Self {
28        let writer = BufferWriter::stderr(color);
29        Self { writer }
30    }
31}
32impl Emitter for DefaultEmitter {
33    #[inline(always)]
34    fn buffer(&self) -> Buffer {
35        self.writer.buffer()
36    }
37
38    #[inline(always)]
39    fn print(&self, buffer: Buffer) -> std::io::Result<()> {
40        self.writer.print(&buffer)
41    }
42}
43
44/// [CaptureEmitter] is used to capture diagnostics which are emitted, for later examination.
45///
46/// This is intended for use in testing, where it is desirable to emit diagnostics
47/// and write assertions about what was displayed to the user.
48#[derive(Default)]
49pub struct CaptureEmitter {
50    buffer: Mutex<Vec<u8>>,
51}
52impl CaptureEmitter {
53    /// Create a new [CaptureEmitter]
54    #[inline]
55    pub fn new() -> Self {
56        Self::default()
57    }
58
59    pub fn captured(&self) -> String {
60        let buf = self.buffer.lock();
61        String::from_utf8_lossy(buf.as_slice()).into_owned()
62    }
63}
64impl Emitter for CaptureEmitter {
65    #[inline]
66    fn buffer(&self) -> Buffer {
67        Buffer::no_color()
68    }
69
70    #[inline]
71    fn print(&self, buffer: Buffer) -> std::io::Result<()> {
72        let mut bytes = buffer.into_inner();
73        let mut buf = self.buffer.lock();
74        buf.append(&mut bytes);
75        Ok(())
76    }
77}
78
79/// [NullEmitter] is used to silence diagnostics entirely, without changing
80/// anything in the diagnostic infrastructure.
81///
82/// When used, the rendered buffer is thrown away.
83#[derive(Clone, Copy, Default)]
84pub struct NullEmitter {
85    ansi: bool,
86}
87impl NullEmitter {
88    pub fn new(color: ColorChoice) -> Self {
89        let ansi = match color {
90            ColorChoice::Never => false,
91            ColorChoice::Always | ColorChoice::AlwaysAnsi => true,
92            ColorChoice::Auto => atty::is(atty::Stream::Stdout),
93        };
94        Self { ansi }
95    }
96}
97impl Emitter for NullEmitter {
98    #[inline(always)]
99    fn buffer(&self) -> Buffer {
100        if self.ansi {
101            Buffer::ansi()
102        } else {
103            Buffer::no_color()
104        }
105    }
106
107    #[inline(always)]
108    fn print(&self, _buffer: Buffer) -> std::io::Result<()> {
109        Ok(())
110    }
111}