1use std::{
4 convert::Infallible,
5 io::{self, IsTerminal as _, Write},
6 str,
11};
12
13use crate::{
14 formatter::{Formatter, FormatterContext},
15 sink::{GetSinkProp, Sink, SinkProp},
16 sync::*,
17 terminal_style::{LevelStyles, Style, StyleMode},
18 Error, ErrorHandler, Level, LevelFilter, Record, Result, StringBuf,
19};
20
21#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
23pub enum StdStream {
24 Stdout,
26 Stderr,
28}
29
30impl StdStream {
31 fn via_write(&self) -> StdStreamDest<io::StdoutLock<'_>, io::StderrLock<'_>> {
32 match self {
33 Self::Stdout => StdStreamDest::Stdout(io::stdout().lock()),
34 Self::Stderr => StdStreamDest::Stderr(io::stderr().lock()),
35 }
36 }
37
38 fn via_macro(&self) -> StdStreamDest<via_macro::Stdout, via_macro::Stderr> {
39 match self {
40 Self::Stdout => StdStreamDest::Stdout(via_macro::Stdout),
41 Self::Stderr => StdStreamDest::Stderr(via_macro::Stderr),
42 }
43 }
44}
45
46#[derive(Debug)]
50enum StdStreamDest<O, E> {
51 Stdout(O),
52 Stderr(E),
53}
54
55impl<O, E> StdStreamDest<O, E> {
56 #[allow(dead_code)]
57 fn stream_type(&self) -> StdStream {
58 match self {
59 Self::Stdout(_) => StdStream::Stdout,
60 Self::Stderr(_) => StdStream::Stderr,
61 }
62 }
63}
64
65macro_rules! impl_write_for_dest {
66 ( $dest:ty ) => {
67 impl Write for $dest {
68 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
69 match self {
70 StdStreamDest::Stdout(stream) => stream.write(buf),
71 StdStreamDest::Stderr(stream) => stream.write(buf),
72 }
73 }
74
75 fn flush(&mut self) -> io::Result<()> {
76 match self {
77 StdStreamDest::Stdout(stream) => stream.flush(),
78 StdStreamDest::Stderr(stream) => stream.flush(),
79 }
80 }
81 }
82 };
83}
84impl_write_for_dest!(StdStreamDest<io::StdoutLock<'_>, io::StderrLock<'_>>);
85impl_write_for_dest!(StdStreamDest<via_macro::Stdout, via_macro::Stderr>);
86
87mod via_macro {
88 use super::*;
89
90 fn bytes_to_str(buf: &[u8]) -> io::Result<&str> {
91 str::from_utf8(buf).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
92 }
93
94 pub(crate) struct Stdout;
95
96 impl Write for Stdout {
97 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
98 print!("{}", bytes_to_str(buf)?);
99 Ok(buf.len())
100 }
101
102 fn flush(&mut self) -> io::Result<()> {
103 io::stdout().flush()
104 }
105 }
106
107 pub(crate) struct Stderr;
108
109 impl Write for Stderr {
110 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
111 eprint!("{}", bytes_to_str(buf)?);
112 Ok(buf.len())
113 }
114
115 fn flush(&mut self) -> io::Result<()> {
116 io::stderr().flush()
117 }
118 }
119}
120
121pub struct StdStreamSink {
128 prop: SinkProp,
129 via_print_macro: bool,
130 std_stream: StdStream,
131 should_render_style: bool,
132 level_styles: LevelStyles,
133}
134
135impl StdStreamSink {
136 #[must_use]
156 pub fn builder() -> StdStreamSinkBuilder<()> {
157 StdStreamSinkBuilder {
158 prop: SinkProp::default(),
159 std_stream: (),
160 style_mode: StyleMode::Auto,
161 via_print_macro: cfg!(feature = "std-stream-captured"),
162 }
163 }
164
165 #[deprecated(
167 since = "0.3.0",
168 note = "it may be removed in the future, use `StdStreamSink::builder()` instead"
169 )]
170 #[must_use]
171 pub fn new(std_stream: StdStream, style_mode: StyleMode) -> StdStreamSink {
172 Self::builder()
173 .std_stream(std_stream)
174 .style_mode(style_mode)
175 .build()
176 .unwrap()
177 }
178
179 pub fn set_style(&mut self, level: Level, style: Style) {
181 self.level_styles.set_style(level, style);
182 }
183
184 pub fn set_style_mode(&mut self, style_mode: StyleMode) {
186 self.should_render_style = Self::should_render_style(style_mode, self.std_stream);
187 }
188
189 #[must_use]
190 fn should_render_style(style_mode: StyleMode, stream: StdStream) -> bool {
191 let is_terminal = match stream {
192 StdStream::Stdout => io::stdout().is_terminal(),
193 StdStream::Stderr => io::stderr().is_terminal(),
194 };
195
196 match style_mode {
197 StyleMode::Always => true,
198 StyleMode::Auto => is_terminal && enable_ansi_escape_sequences(),
199 StyleMode::Never => false,
200 }
201 }
202}
203
204impl GetSinkProp for StdStreamSink {
205 fn prop(&self) -> &SinkProp {
206 &self.prop
207 }
208}
209
210impl Sink for StdStreamSink {
211 fn log(&self, record: &Record) -> Result<()> {
212 let mut string_buf = StringBuf::new();
213 let mut ctx = FormatterContext::new();
214 self.prop
215 .formatter()
216 .format(record, &mut string_buf, &mut ctx)?;
217
218 if !self.via_print_macro {
219 self.log_write(record, &string_buf, &ctx, self.std_stream.via_write())
220 } else {
221 self.log_write(record, &string_buf, &ctx, self.std_stream.via_macro())
222 }
223 }
224
225 fn flush(&self) -> Result<()> {
226 if !self.via_print_macro {
227 self.std_stream.via_write().flush()
228 } else {
229 self.std_stream.via_macro().flush()
230 }
231 .map_err(Error::FlushBuffer)
232 }
233}
234
235impl StdStreamSink {
236 fn log_write<O: Write, E: Write>(
237 &self,
238 record: &Record,
239 string_buf: &StringBuf,
240 ctx: &FormatterContext<'_>,
241 mut dest: StdStreamDest<O, E>,
242 ) -> Result<()>
243 where
244 StdStreamDest<O, E>: Write,
245 {
246 (|| {
247 if self.should_render_style {
249 if let Some(style_range) = ctx.style_range() {
250 let style = self.level_styles.style(record.level());
251
252 dest.write_all(string_buf[..style_range.start].as_bytes())?;
253 style.write_start(&mut dest)?;
254 dest.write_all(string_buf[style_range.start..style_range.end].as_bytes())?;
255 style.write_end(&mut dest)?;
256 dest.write_all(string_buf[style_range.end..].as_bytes())?;
257 } else {
258 dest.write_all(string_buf.as_bytes())?;
259 }
260 } else {
261 dest.write_all(string_buf.as_bytes())?;
262 }
263
264 Ok(())
265 })()
266 .map_err(Error::WriteRecord)?;
267
268 if let StdStream::Stdout = self.std_stream {
271 dest.flush().map_err(Error::FlushBuffer)?;
272 }
273
274 Ok(())
275 }
276}
277
278#[doc = include_str!("../include/doc/generic-builder-note.md")]
282pub struct StdStreamSinkBuilder<ArgSS> {
283 prop: SinkProp,
284 std_stream: ArgSS,
285 style_mode: StyleMode,
286 via_print_macro: bool,
287}
288
289impl<ArgSS> StdStreamSinkBuilder<ArgSS> {
290 #[must_use]
294 pub fn stdout(self) -> StdStreamSinkBuilder<StdStream> {
295 self.std_stream(StdStream::Stdout)
296 }
297
298 #[must_use]
302 pub fn stderr(self) -> StdStreamSinkBuilder<StdStream> {
303 self.std_stream(StdStream::Stderr)
304 }
305
306 #[must_use]
310 pub fn std_stream(self, std_stream: StdStream) -> StdStreamSinkBuilder<StdStream> {
311 StdStreamSinkBuilder {
312 prop: self.prop,
313 std_stream,
314 style_mode: self.style_mode,
315 via_print_macro: self.via_print_macro,
316 }
317 }
318
319 #[must_use]
323 pub fn style_mode(mut self, style_mode: StyleMode) -> Self {
324 self.style_mode = style_mode;
325 self
326 }
327
328 #[must_use]
350 pub fn via_print_macro(mut self) -> Self {
351 self.via_print_macro = true;
352 self
353 }
354
355 #[must_use]
362 pub fn level_filter(self, level_filter: LevelFilter) -> Self {
363 self.prop.set_level_filter(level_filter);
364 self
365 }
366
367 #[must_use]
373 pub fn formatter<F>(self, formatter: F) -> Self
374 where
375 F: Formatter + 'static,
376 {
377 self.prop.set_formatter(formatter);
378 self
379 }
380
381 #[must_use]
386 pub fn error_handler<F: Into<ErrorHandler>>(self, handler: F) -> Self {
387 self.prop.set_error_handler(handler);
388 self
389 }
390}
391
392impl StdStreamSinkBuilder<()> {
393 #[doc(hidden)]
394 #[deprecated(note = "\n\n\
395 builder compile-time error:\n\
396 - missing required parameter `std_stream`\n\n\
397 ")]
398 pub fn build(self, _: Infallible) {}
399
400 #[doc(hidden)]
401 #[deprecated(note = "\n\n\
402 builder compile-time error:\n\
403 - missing required parameter `std_stream`\n\n\
404 ")]
405 pub fn build_arc(self, _: Infallible) {}
406}
407
408impl StdStreamSinkBuilder<StdStream> {
409 pub fn build(self) -> Result<StdStreamSink> {
411 Ok(StdStreamSink {
412 prop: self.prop,
413 via_print_macro: self.via_print_macro,
414 std_stream: self.std_stream,
415 should_render_style: StdStreamSink::should_render_style(
416 self.style_mode,
417 self.std_stream,
418 ),
419 level_styles: LevelStyles::default(),
420 })
421 }
422
423 pub fn build_arc(self) -> Result<Arc<StdStreamSink>> {
427 self.build().map(Arc::new)
428 }
429}
430
431#[cfg(windows)]
433#[must_use]
434fn enable_ansi_escape_sequences() -> bool {
435 #[must_use]
436 fn enable() -> bool {
437 use winapi::um::{
438 consoleapi::{GetConsoleMode, SetConsoleMode},
439 handleapi::INVALID_HANDLE_VALUE,
440 processenv::GetStdHandle,
441 winbase::STD_OUTPUT_HANDLE,
442 wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING,
443 };
444
445 let stdout_handle = unsafe { GetStdHandle(STD_OUTPUT_HANDLE) };
446 if stdout_handle == INVALID_HANDLE_VALUE {
447 return false;
448 }
449
450 let mut original_mode = 0;
451 if unsafe { GetConsoleMode(stdout_handle, &mut original_mode) } == 0 {
452 return false;
453 }
454
455 let new_mode = original_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
456 (unsafe { SetConsoleMode(stdout_handle, new_mode) }) != 0
457 }
458
459 use once_cell::sync::OnceCell;
460
461 static INIT: OnceCell<bool> = OnceCell::new();
462
463 *INIT.get_or_init(enable)
464}
465
466#[cfg(not(windows))]
467#[must_use]
468fn enable_ansi_escape_sequences() -> bool {
469 true
470}