33use std:: {
44 convert:: Infallible ,
55 io:: { self , Write } ,
6+ // Import `str` module for function `std::str::from_utf8`, because method `str::from_utf8` is
7+ // stabilized since Rust 1.87.
8+ //
9+ // TODO: Remove this import when our MSRV reaches Rust 1.87.
10+ str,
611} ;
712
813use crate :: {
@@ -21,6 +26,22 @@ pub enum StdStream {
2126 Stderr ,
2227}
2328
29+ impl StdStream {
30+ fn via_write ( & self ) -> StdStreamDest < io:: StdoutLock < ' _ > , io:: StderrLock < ' _ > > {
31+ match self {
32+ Self :: Stdout => StdStreamDest :: Stdout ( io:: stdout ( ) . lock ( ) ) ,
33+ Self :: Stderr => StdStreamDest :: Stderr ( io:: stderr ( ) . lock ( ) ) ,
34+ }
35+ }
36+
37+ fn via_macro ( & self ) -> StdStreamDest < via_macro:: Stdout , via_macro:: Stderr > {
38+ match self {
39+ Self :: Stdout => StdStreamDest :: Stdout ( via_macro:: Stdout ) ,
40+ Self :: Stderr => StdStreamDest :: Stderr ( via_macro:: Stderr ) ,
41+ }
42+ }
43+ }
44+
2445// `io::stdout()` and `io::stderr()` return different types, and
2546// `Std***::lock()` is not in any trait, so we need this struct to abstract
2647// them.
@@ -30,27 +51,12 @@ enum StdStreamDest<O, E> {
3051 Stderr ( E ) ,
3152}
3253
33- impl StdStreamDest < io:: Stdout , io:: Stderr > {
34- #[ must_use]
35- fn new ( stream : StdStream ) -> Self {
36- match stream {
37- StdStream :: Stdout => StdStreamDest :: Stdout ( io:: stdout ( ) ) ,
38- StdStream :: Stderr => StdStreamDest :: Stderr ( io:: stderr ( ) ) ,
39- }
40- }
41-
42- #[ must_use]
43- fn lock ( & self ) -> StdStreamDest < io:: StdoutLock < ' _ > , io:: StderrLock < ' _ > > {
44- match self {
45- StdStreamDest :: Stdout ( stream) => StdStreamDest :: Stdout ( stream. lock ( ) ) ,
46- StdStreamDest :: Stderr ( stream) => StdStreamDest :: Stderr ( stream. lock ( ) ) ,
47- }
48- }
49-
54+ impl < O , E > StdStreamDest < O , E > {
55+ #[ allow( dead_code) ]
5056 fn stream_type ( & self ) -> StdStream {
5157 match self {
52- StdStreamDest :: Stdout ( _) => StdStream :: Stdout ,
53- StdStreamDest :: Stderr ( _) => StdStream :: Stderr ,
58+ Self :: Stdout ( _) => StdStream :: Stdout ,
59+ Self :: Stderr ( _) => StdStream :: Stderr ,
5460 }
5561 }
5662}
@@ -74,8 +80,42 @@ macro_rules! impl_write_for_dest {
7480 }
7581 } ;
7682}
77- impl_write_for_dest ! ( StdStreamDest <io:: Stdout , io:: Stderr >) ;
7883impl_write_for_dest ! ( StdStreamDest <io:: StdoutLock <' _>, io:: StderrLock <' _>>) ;
84+ impl_write_for_dest ! ( StdStreamDest <via_macro:: Stdout , via_macro:: Stderr >) ;
85+
86+ mod via_macro {
87+ use super :: * ;
88+
89+ fn bytes_to_str ( buf : & [ u8 ] ) -> io:: Result < & str > {
90+ str:: from_utf8 ( buf) . map_err ( |err| io:: Error :: new ( io:: ErrorKind :: InvalidData , err) )
91+ }
92+
93+ pub ( crate ) struct Stdout ;
94+
95+ impl Write for Stdout {
96+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
97+ print ! ( "{}" , bytes_to_str( buf) ?) ;
98+ Ok ( buf. len ( ) )
99+ }
100+
101+ fn flush ( & mut self ) -> io:: Result < ( ) > {
102+ io:: stdout ( ) . flush ( )
103+ }
104+ }
105+
106+ pub ( crate ) struct Stderr ;
107+
108+ impl Write for Stderr {
109+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
110+ eprint ! ( "{}" , bytes_to_str( buf) ?) ;
111+ Ok ( buf. len ( ) )
112+ }
113+
114+ fn flush ( & mut self ) -> io:: Result < ( ) > {
115+ io:: stderr ( ) . flush ( )
116+ }
117+ }
118+ }
79119
80120/// A sink with a std stream as the target.
81121///
@@ -85,35 +125,39 @@ impl_write_for_dest!(StdStreamDest<io::StdoutLock<'_>, io::StderrLock<'_>>);
85125/// Note that this sink always flushes the buffer once with each logging.
86126pub struct StdStreamSink {
87127 prop : SinkProp ,
88- dest : StdStreamDest < io:: Stdout , io:: Stderr > ,
128+ via_print_macro : bool ,
129+ std_stream : StdStream ,
89130 should_render_style : bool ,
90131 level_styles : LevelStyles ,
91132}
92133
93134impl StdStreamSink {
94135 /// Gets a builder of `StdStreamSink` with default parameters:
95136 ///
96- /// | Parameter | Default Value |
97- /// |-------------------|-----------------------------|
98- /// | [level_filter] | `All` |
99- /// | [formatter] | `FullFormatter` |
100- /// | [error_handler] | [`ErrorHandler::default()`] |
101- /// | | |
102- /// | [std_stream] | *must be specified* |
103- /// | [style_mode] | `Auto` |
137+ /// | Parameter | Default Value |
138+ /// |-------------------|------------------------------------------------------|
139+ /// | [level_filter] | `All` |
140+ /// | [formatter] | `FullFormatter` |
141+ /// | [error_handler] | [`ErrorHandler::default()`] |
142+ /// | | |
143+ /// | [std_stream] | *must be specified* |
144+ /// | [style_mode] | `Auto` |
145+ /// | [via_print_macro] | `false`, or `true` if feature gate `test` is enabled |
104146 ///
105147 /// [level_filter]: StdStreamSinkBuilder::level_filter
106148 /// [formatter]: StdStreamSinkBuilder::formatter
107149 /// [error_handler]: StdStreamSinkBuilder::error_handler
108150 /// [`ErrorHandler::default()`]: crate::error::ErrorHandler::default()
109151 /// [std_stream]: StdStreamSinkBuilder::std_stream
110152 /// [style_mode]: StdStreamSinkBuilder::style_mode
153+ /// [via_print_macro]: StdStreamSinkBuilder::via_print_macro
111154 #[ must_use]
112155 pub fn builder ( ) -> StdStreamSinkBuilder < ( ) > {
113156 StdStreamSinkBuilder {
114157 prop : SinkProp :: default ( ) ,
115158 std_stream : ( ) ,
116159 style_mode : StyleMode :: Auto ,
160+ via_print_macro : cfg ! ( feature = "test" ) ,
117161 }
118162 }
119163
@@ -138,7 +182,7 @@ impl StdStreamSink {
138182
139183 /// Sets the style mode.
140184 pub fn set_style_mode ( & mut self , style_mode : StyleMode ) {
141- self . should_render_style = Self :: should_render_style ( style_mode, self . dest . stream_type ( ) ) ;
185+ self . should_render_style = Self :: should_render_style ( style_mode, self . std_stream ) ;
142186 }
143187
144188 #[ must_use]
@@ -171,8 +215,34 @@ impl Sink for StdStreamSink {
171215 . formatter ( )
172216 . format ( record, & mut string_buf, & mut ctx) ?;
173217
174- let mut dest = self . dest . lock ( ) ;
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+ }
175224
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+
235+ impl 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+ {
176246 ( || {
177247 // TODO: Simplify the if block when our MSRV reaches let-chain support.
178248 if self . should_render_style {
@@ -197,16 +267,12 @@ impl Sink for StdStreamSink {
197267
198268 // stderr is not buffered, so we don't need to flush it.
199269 // https://doc.rust-lang.org/std/io/fn.stderr.html
200- if let StdStreamDest :: Stdout ( _ ) = dest {
270+ if let StdStream :: Stdout = self . std_stream {
201271 dest. flush ( ) . map_err ( Error :: FlushBuffer ) ?;
202272 }
203273
204274 Ok ( ( ) )
205275 }
206-
207- fn flush ( & self ) -> Result < ( ) > {
208- self . dest . lock ( ) . flush ( ) . map_err ( Error :: FlushBuffer )
209- }
210276}
211277
212278// --------------------------------------------------
@@ -217,6 +283,7 @@ pub struct StdStreamSinkBuilder<ArgSS> {
217283 prop : SinkProp ,
218284 std_stream : ArgSS ,
219285 style_mode : StyleMode ,
286+ via_print_macro : bool ,
220287}
221288
222289impl < ArgSS > StdStreamSinkBuilder < ArgSS > {
@@ -245,6 +312,7 @@ impl<ArgSS> StdStreamSinkBuilder<ArgSS> {
245312 prop : self . prop ,
246313 std_stream,
247314 style_mode : self . style_mode ,
315+ via_print_macro : self . via_print_macro ,
248316 }
249317 }
250318
@@ -257,6 +325,33 @@ impl<ArgSS> StdStreamSinkBuilder<ArgSS> {
257325 self
258326 }
259327
328+ /// Specifies to use `print!` and `eprint!` macros for output.
329+ ///
330+ /// If enabled, the sink will use [`print!`] and [`eprint!`] macros instead
331+ /// of [`io::Write`] trait with [`io::stdout`] and [`io::stderr`] to output
332+ /// logs. This is useful if you want the logs to be [captured] by `cargo
333+ /// test` and `cargo bench`.
334+ ///
335+ /// This parameter is **optional**, and defaults to `false`, or defaults to
336+ /// `true` if feature gate `test` is enabled.
337+ ///
338+ /// A convienient way to enable it for `cargo test` and `cargo bench` is to
339+ /// add the following lines to your `Cargo.toml`:
340+ ///
341+ /// ```toml
342+ /// # Note that it's not [dependencies]
343+ ///
344+ /// [dev-dependencies]
345+ /// spdlog-rs = { version = "...", features = ["test"] }
346+ /// ```
347+ ///
348+ /// [captured]: https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output
349+ #[ must_use]
350+ pub fn via_print_macro ( mut self ) -> Self {
351+ self . via_print_macro = true ;
352+ self
353+ }
354+
260355 // Prop
261356 //
262357
@@ -305,7 +400,8 @@ impl StdStreamSinkBuilder<StdStream> {
305400 pub fn build ( self ) -> Result < StdStreamSink > {
306401 Ok ( StdStreamSink {
307402 prop : self . prop ,
308- dest : StdStreamDest :: new ( self . std_stream ) ,
403+ via_print_macro : self . via_print_macro ,
404+ std_stream : self . std_stream ,
309405 should_render_style : StdStreamSink :: should_render_style (
310406 self . style_mode ,
311407 self . std_stream ,
0 commit comments