midenc_session/
emitter.rs

1use alloc::{string::String, vec::Vec};
2use core::ops::Deref;
3
4use crate::{
5    diagnostics::{IntoDiagnostic, Report},
6    ColorChoice,
7};
8
9/// The [Emitter] trait is used for controlling how diagnostics are displayed.
10///
11/// An [Emitter] must produce a [Buffer] for use by the rendering
12/// internals, and its own print implementation.
13///
14/// When a diagnostic is being emitted, a new [Buffer] is allocated,
15/// the diagnostic is rendered into it, and then the buffer is passed
16/// to `print` for display by the [Emitter] implementation.
17pub trait Emitter: Send + Sync {
18    /// Construct a new [Buffer] for use by the renderer
19    fn buffer(&self) -> Buffer;
20    /// Display the contents of the given [Buffer]
21    fn print(&self, buffer: Buffer) -> Result<(), Report>;
22}
23
24/// [DefaultEmitter] is used for rendering to stderr, and as is implied
25/// by the name, is the default emitter implementation.
26pub struct DefaultEmitter(DefaultEmitterImpl);
27impl DefaultEmitter {
28    /// Construct a new [DefaultEmitter] with the given [ColorChoice] behavior.
29    pub fn new(color: ColorChoice) -> Self {
30        Self(DefaultEmitterImpl::new(color))
31    }
32}
33impl Emitter for DefaultEmitter {
34    #[inline(always)]
35    fn buffer(&self) -> Buffer {
36        self.0.buffer()
37    }
38
39    #[inline(always)]
40    fn print(&self, buffer: Buffer) -> Result<(), Report> {
41        self.0.print(buffer)
42    }
43}
44impl Deref for DefaultEmitter {
45    type Target = DefaultEmitterImpl;
46
47    #[inline(always)]
48    fn deref(&self) -> &Self::Target {
49        &self.0
50    }
51}
52
53#[cfg(feature = "std")]
54#[doc(hidden)]
55pub struct DefaultEmitterImpl {
56    writer: termcolor::BufferWriter,
57}
58
59#[cfg(not(feature = "std"))]
60#[doc(hidden)]
61pub struct DefaultEmitterImpl {
62    writer: Vec<u8>,
63    ansi: bool,
64}
65
66#[cfg(feature = "std")]
67impl DefaultEmitterImpl {
68    fn new(color: ColorChoice) -> Self {
69        Self {
70            writer: termcolor::BufferWriter::stderr(color.into()),
71        }
72    }
73}
74
75#[cfg(feature = "std")]
76impl Emitter for DefaultEmitterImpl {
77    #[inline(always)]
78    fn buffer(&self) -> Buffer {
79        Buffer(self.writer.buffer())
80    }
81
82    #[inline(always)]
83    fn print(&self, buffer: Buffer) -> Result<(), Report> {
84        self.writer.print(&buffer.0).into_diagnostic()
85    }
86}
87
88#[cfg(not(feature = "std"))]
89impl DefaultEmitterImpl {
90    fn new(color: ColorChoice) -> Self {
91        Self {
92            ansi: color.should_ansi(),
93            writer: vec![],
94        }
95    }
96}
97
98#[cfg(not(feature = "std"))]
99impl Emitter for DefaultEmitterImpl {
100    #[inline(always)]
101    fn buffer(&self) -> Buffer {
102        if self.ansi {
103            Buffer::ansi()
104        } else {
105            Buffer::no_color()
106        }
107    }
108
109    #[inline(always)]
110    fn print(&self, buffer: Buffer) -> Result<(), Report> {
111        self.0.push(b'\n');
112        self.0.extend(buffer.into_inner());
113        Ok(())
114    }
115}
116
117/// [CaptureEmitter] is used to capture diagnostics which are emitted, for later examination.
118///
119/// This is intended for use in testing, where it is desirable to emit diagnostics
120/// and write assertions about what was displayed to the user.
121#[derive(Default)]
122#[cfg(feature = "std")]
123pub struct CaptureEmitter {
124    buffer: parking_lot::Mutex<Vec<u8>>,
125}
126#[cfg(feature = "std")]
127impl CaptureEmitter {
128    /// Create a new [CaptureEmitter]
129    #[inline]
130    pub fn new() -> Self {
131        Self::default()
132    }
133
134    pub fn captured(&self) -> String {
135        let buf = self.buffer.lock();
136        String::from_utf8_lossy(buf.as_slice()).into_owned()
137    }
138}
139#[cfg(feature = "std")]
140impl Emitter for CaptureEmitter {
141    #[inline]
142    fn buffer(&self) -> Buffer {
143        Buffer::no_color()
144    }
145
146    #[inline]
147    fn print(&self, buffer: Buffer) -> Result<(), Report> {
148        let mut bytes = buffer.into_inner();
149        let mut buf = self.buffer.lock();
150        buf.append(&mut bytes);
151        Ok(())
152    }
153}
154
155/// [NullEmitter] is used to silence diagnostics entirely, without changing
156/// anything in the diagnostic infrastructure.
157///
158/// When used, the rendered buffer is thrown away.
159#[derive(Clone, Copy, Default)]
160pub struct NullEmitter {
161    ansi: bool,
162}
163impl NullEmitter {
164    #[cfg(feature = "std")]
165    pub fn new(color: ColorChoice) -> Self {
166        use std::io::IsTerminal;
167
168        let ansi = match color {
169            ColorChoice::Never => false,
170            ColorChoice::Always | ColorChoice::AlwaysAnsi => true,
171            ColorChoice::Auto => std::io::stdout().is_terminal(),
172        };
173        Self { ansi }
174    }
175
176    #[cfg(not(feature = "std"))]
177    pub fn new(color: ColorChoice) -> Self {
178        let ansi = match color {
179            ColorChoice::Never => false,
180            ColorChoice::Always | ColorChoice::AlwaysAnsi => true,
181            ColorChoice::Auto => false,
182        };
183        Self { ansi }
184    }
185}
186impl Emitter for NullEmitter {
187    #[inline(always)]
188    fn buffer(&self) -> Buffer {
189        if self.ansi {
190            Buffer::ansi()
191        } else {
192            Buffer::no_color()
193        }
194    }
195
196    #[inline(always)]
197    fn print(&self, _buffer: Buffer) -> Result<(), Report> {
198        Ok(())
199    }
200}
201
202#[doc(hidden)]
203#[cfg(not(feature = "std"))]
204#[derive(Clone, Debug)]
205pub struct Buffer(Vec<u8>);
206
207#[doc(hidden)]
208#[cfg(feature = "std")]
209#[derive(Clone, Debug)]
210pub struct Buffer(termcolor::Buffer);
211
212impl Buffer {
213    /// Create a new buffer with the given color settings.
214    #[cfg(not(feature = "std"))]
215    pub fn new(_choice: ColorChoice) -> Buffer {
216        Self::no_color()
217    }
218
219    #[cfg(feature = "std")]
220    pub fn new(choice: ColorChoice) -> Buffer {
221        match choice {
222            ColorChoice::Never => Self::no_color(),
223            ColorChoice::Auto => {
224                if choice.should_attempt_color() {
225                    Self::ansi()
226                } else {
227                    Self::no_color()
228                }
229            }
230            ColorChoice::Always | ColorChoice::AlwaysAnsi => Self::ansi(),
231        }
232    }
233
234    /// Create a buffer that drops all color information.
235    #[cfg(not(feature = "std"))]
236    pub fn no_color() -> Buffer {
237        Self(vec![])
238    }
239
240    /// Create a buffer that drops all color information.
241    #[cfg(feature = "std")]
242    pub fn no_color() -> Buffer {
243        Self(termcolor::Buffer::no_color())
244    }
245
246    /// Create a buffer that uses ANSI escape sequences.
247    #[cfg(not(feature = "std"))]
248    pub fn ansi() -> Buffer {
249        Buffer(vec![])
250    }
251
252    /// Create a buffer that uses ANSI escape sequences.
253    #[cfg(feature = "std")]
254    pub fn ansi() -> Buffer {
255        Self(termcolor::Buffer::ansi())
256    }
257
258    /// Returns true if and only if this buffer is empty.
259    pub fn is_empty(&self) -> bool {
260        self.len() == 0
261    }
262
263    /// Returns the length of this buffer in bytes.
264    #[inline]
265    pub fn len(&self) -> usize {
266        self.0.len()
267    }
268
269    /// Clears this buffer.
270    #[inline]
271    pub fn clear(&mut self) {
272        self.0.clear()
273    }
274
275    /// Consume this buffer and return the underlying raw data.
276    ///
277    /// On Windows, this unrecoverably drops all color information associated
278    /// with the buffer.
279    #[inline(always)]
280    #[cfg(not(feature = "std"))]
281    pub fn into_inner(self) -> Vec<u8> {
282        self.0
283    }
284
285    #[cfg(feature = "std")]
286    pub fn into_inner(self) -> Vec<u8> {
287        self.0.into_inner()
288    }
289
290    /// Return the underlying data of the buffer.
291    #[inline(always)]
292    pub fn as_slice(&self) -> &[u8] {
293        self.0.as_slice()
294    }
295
296    /// Return the underlying data of the buffer as a mutable slice.
297    #[inline(always)]
298    pub fn as_mut_slice(&mut self) -> &mut [u8] {
299        self.0.as_mut_slice()
300    }
301}
302
303#[cfg(not(feature = "std"))]
304impl core::fmt::Write for Buffer {
305    fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Result> {
306        use core::fmt::Write;
307        self.0.write_str(s);
308    }
309}
310
311#[cfg(feature = "std")]
312impl std::io::Write for Buffer {
313    #[inline]
314    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
315        self.0.write(buf)
316    }
317
318    #[inline]
319    fn flush(&mut self) -> std::io::Result<()> {
320        self.0.flush()
321    }
322}