midenc_session/
emitter.rs

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