midenc_session/
emitter.rs1use alloc::{string::String, vec::Vec};
2use core::ops::Deref;
3
4use crate::{
5 diagnostics::{IntoDiagnostic, Report},
6 ColorChoice,
7};
8
9pub trait Emitter: Send + Sync {
18 fn buffer(&self) -> Buffer;
20 fn print(&self, buffer: Buffer) -> Result<(), Report>;
22}
23
24pub struct DefaultEmitter(DefaultEmitterImpl);
27impl DefaultEmitter {
28 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#[derive(Default)]
122#[cfg(feature = "std")]
123pub struct CaptureEmitter {
124 buffer: parking_lot::Mutex<Vec<u8>>,
125}
126#[cfg(feature = "std")]
127impl CaptureEmitter {
128 #[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#[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 #[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 #[cfg(not(feature = "std"))]
236 pub fn no_color() -> Buffer {
237 Self(vec![])
238 }
239
240 #[cfg(feature = "std")]
242 pub fn no_color() -> Buffer {
243 Self(termcolor::Buffer::no_color())
244 }
245
246 #[cfg(not(feature = "std"))]
248 pub fn ansi() -> Buffer {
249 Buffer(vec![])
250 }
251
252 #[cfg(feature = "std")]
254 pub fn ansi() -> Buffer {
255 Self(termcolor::Buffer::ansi())
256 }
257
258 pub fn is_empty(&self) -> bool {
260 self.len() == 0
261 }
262
263 #[inline]
265 pub fn len(&self) -> usize {
266 self.0.len()
267 }
268
269 #[inline]
271 pub fn clear(&mut self) {
272 self.0.clear()
273 }
274
275 #[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 #[inline(always)]
292 pub fn as_slice(&self) -> &[u8] {
293 self.0.as_slice()
294 }
295
296 #[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}