33use std:: {
44 convert:: Infallible ,
55 io:: { self , Write } ,
6+ str,
67} ;
78
89use crate :: {
@@ -21,6 +22,22 @@ pub enum StdStream {
2122 Stderr ,
2223}
2324
25+ impl StdStream {
26+ fn via_write ( & self ) -> StdStreamDest < io:: StdoutLock < ' _ > , io:: StderrLock < ' _ > > {
27+ match self {
28+ Self :: Stdout => StdStreamDest :: Stdout ( io:: stdout ( ) . lock ( ) ) ,
29+ Self :: Stderr => StdStreamDest :: Stderr ( io:: stderr ( ) . lock ( ) ) ,
30+ }
31+ }
32+
33+ fn via_macro ( & self ) -> StdStreamDest < via_macro:: Stdout , via_macro:: Stderr > {
34+ match self {
35+ Self :: Stdout => StdStreamDest :: Stdout ( via_macro:: Stdout ) ,
36+ Self :: Stderr => StdStreamDest :: Stderr ( via_macro:: Stderr ) ,
37+ }
38+ }
39+ }
40+
2441// `io::stdout()` and `io::stderr()` return different types, and
2542// `Std***::lock()` is not in any trait, so we need this struct to abstract
2643// them.
@@ -30,27 +47,12 @@ enum StdStreamDest<O, E> {
3047 Stderr ( E ) ,
3148}
3249
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-
50+ impl < O , E > StdStreamDest < O , E > {
51+ #[ allow( dead_code) ]
5052 fn stream_type ( & self ) -> StdStream {
5153 match self {
52- StdStreamDest :: Stdout ( _) => StdStream :: Stdout ,
53- StdStreamDest :: Stderr ( _) => StdStream :: Stderr ,
54+ Self :: Stdout ( _) => StdStream :: Stdout ,
55+ Self :: Stderr ( _) => StdStream :: Stderr ,
5456 }
5557 }
5658}
@@ -74,8 +76,42 @@ macro_rules! impl_write_for_dest {
7476 }
7577 } ;
7678}
77- impl_write_for_dest ! ( StdStreamDest <io:: Stdout , io:: Stderr >) ;
7879impl_write_for_dest ! ( StdStreamDest <io:: StdoutLock <' _>, io:: StderrLock <' _>>) ;
80+ impl_write_for_dest ! ( StdStreamDest <via_macro:: Stdout , via_macro:: Stderr >) ;
81+
82+ mod via_macro {
83+ use super :: * ;
84+
85+ fn bytes_to_str ( buf : & [ u8 ] ) -> io:: Result < & str > {
86+ str:: from_utf8 ( buf) . map_err ( |err| io:: Error :: new ( io:: ErrorKind :: InvalidData , err) )
87+ }
88+
89+ pub ( crate ) struct Stdout ;
90+
91+ impl Write for Stdout {
92+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
93+ print ! ( "{}" , bytes_to_str( buf) ?) ;
94+ Ok ( buf. len ( ) )
95+ }
96+
97+ fn flush ( & mut self ) -> io:: Result < ( ) > {
98+ io:: stdout ( ) . flush ( )
99+ }
100+ }
101+
102+ pub ( crate ) struct Stderr ;
103+
104+ impl Write for Stderr {
105+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
106+ eprint ! ( "{}" , bytes_to_str( buf) ?) ;
107+ Ok ( buf. len ( ) )
108+ }
109+
110+ fn flush ( & mut self ) -> io:: Result < ( ) > {
111+ io:: stderr ( ) . flush ( )
112+ }
113+ }
114+ }
79115
80116/// A sink with a std stream as the target.
81117///
@@ -85,35 +121,39 @@ impl_write_for_dest!(StdStreamDest<io::StdoutLock<'_>, io::StderrLock<'_>>);
85121/// Note that this sink always flushes the buffer once with each logging.
86122pub struct StdStreamSink {
87123 prop : SinkProp ,
88- dest : StdStreamDest < io:: Stdout , io:: Stderr > ,
124+ via_print_macro : bool ,
125+ std_stream : StdStream ,
89126 should_render_style : bool ,
90127 level_styles : LevelStyles ,
91128}
92129
93130impl StdStreamSink {
94131 /// Gets a builder of `StdStreamSink` with default parameters:
95132 ///
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` |
133+ /// | Parameter | Default Value |
134+ /// |-------------------|------------------------------------------------------|
135+ /// | [level_filter] | `All` |
136+ /// | [formatter] | `FullFormatter` |
137+ /// | [error_handler] | [`ErrorHandler::default()`] |
138+ /// | | |
139+ /// | [std_stream] | *must be specified* |
140+ /// | [style_mode] | `Auto` |
141+ /// | [via_print_macro] | `false`, or `true` if feature gate `test` is enabled |
104142 ///
105143 /// [level_filter]: StdStreamSinkBuilder::level_filter
106144 /// [formatter]: StdStreamSinkBuilder::formatter
107145 /// [error_handler]: StdStreamSinkBuilder::error_handler
108146 /// [`ErrorHandler::default()`]: crate::error::ErrorHandler::default()
109147 /// [std_stream]: StdStreamSinkBuilder::std_stream
110148 /// [style_mode]: StdStreamSinkBuilder::style_mode
149+ /// [via_print_macro]: StdStreamSinkBuilder::via_print_macro
111150 #[ must_use]
112151 pub fn builder ( ) -> StdStreamSinkBuilder < ( ) > {
113152 StdStreamSinkBuilder {
114153 prop : SinkProp :: default ( ) ,
115154 std_stream : ( ) ,
116155 style_mode : StyleMode :: Auto ,
156+ via_print_macro : cfg ! ( feature = "test" ) ,
117157 }
118158 }
119159
@@ -138,7 +178,7 @@ impl StdStreamSink {
138178
139179 /// Sets the style mode.
140180 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 ( ) ) ;
181+ self . should_render_style = Self :: should_render_style ( style_mode, self . std_stream ) ;
142182 }
143183
144184 #[ must_use]
@@ -171,8 +211,34 @@ impl Sink for StdStreamSink {
171211 . formatter ( )
172212 . format ( record, & mut string_buf, & mut ctx) ?;
173213
174- let mut dest = self . dest . lock ( ) ;
214+ if !self . via_print_macro {
215+ self . log_write ( record, & string_buf, & ctx, self . std_stream . via_write ( ) )
216+ } else {
217+ self . log_write ( record, & string_buf, & ctx, self . std_stream . via_macro ( ) )
218+ }
219+ }
220+
221+ fn flush ( & self ) -> Result < ( ) > {
222+ if !self . via_print_macro {
223+ self . std_stream . via_write ( ) . flush ( )
224+ } else {
225+ self . std_stream . via_macro ( ) . flush ( )
226+ }
227+ . map_err ( Error :: FlushBuffer )
228+ }
229+ }
175230
231+ impl StdStreamSink {
232+ fn log_write < O : Write , E : Write > (
233+ & self ,
234+ record : & Record ,
235+ string_buf : & StringBuf ,
236+ ctx : & FormatterContext < ' _ > ,
237+ mut dest : StdStreamDest < O , E > ,
238+ ) -> Result < ( ) >
239+ where
240+ StdStreamDest < O , E > : Write ,
241+ {
176242 ( || {
177243 // TODO: Simplify the if block when our MSRV reaches let-chain support.
178244 if self . should_render_style {
@@ -197,16 +263,12 @@ impl Sink for StdStreamSink {
197263
198264 // stderr is not buffered, so we don't need to flush it.
199265 // https://doc.rust-lang.org/std/io/fn.stderr.html
200- if let StdStreamDest :: Stdout ( _ ) = dest {
266+ if let StdStream :: Stdout = self . std_stream {
201267 dest. flush ( ) . map_err ( Error :: FlushBuffer ) ?;
202268 }
203269
204270 Ok ( ( ) )
205271 }
206-
207- fn flush ( & self ) -> Result < ( ) > {
208- self . dest . lock ( ) . flush ( ) . map_err ( Error :: FlushBuffer )
209- }
210272}
211273
212274// --------------------------------------------------
@@ -217,6 +279,7 @@ pub struct StdStreamSinkBuilder<ArgSS> {
217279 prop : SinkProp ,
218280 std_stream : ArgSS ,
219281 style_mode : StyleMode ,
282+ via_print_macro : bool ,
220283}
221284
222285impl < ArgSS > StdStreamSinkBuilder < ArgSS > {
@@ -245,6 +308,7 @@ impl<ArgSS> StdStreamSinkBuilder<ArgSS> {
245308 prop : self . prop ,
246309 std_stream,
247310 style_mode : self . style_mode ,
311+ via_print_macro : self . via_print_macro ,
248312 }
249313 }
250314
@@ -257,6 +321,33 @@ impl<ArgSS> StdStreamSinkBuilder<ArgSS> {
257321 self
258322 }
259323
324+ /// Specifies to use `print!` and `eprint!` macros for output.
325+ ///
326+ /// If enabled, the sink will use [`print!`] and [`eprint!`] macros instead
327+ /// of [`io::Write`] trait with [`io::stdout`] and [`io::stderr`] to output
328+ /// logs. This is useful if you want the logs to be [captured] by `cargo
329+ /// test` and `cargo bench`.
330+ ///
331+ /// This parameter is **optional**, and defaults to `false`, or defaults to
332+ /// `true` if feature gate `test` is enabled.
333+ ///
334+ /// A convienient way to enable it for `cargo test` and `cargo bench` is to
335+ /// add the following lines to your `Cargo.toml`:
336+ ///
337+ /// ```toml
338+ /// # Note that it's not [dependencies]
339+ ///
340+ /// [dev-dependencies]
341+ /// spdlog-rs = { version = "...", features = ["test"] }
342+ /// ```
343+ ///
344+ /// [captured]: https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output
345+ #[ must_use]
346+ pub fn via_print_macro ( mut self ) -> Self {
347+ self . via_print_macro = true ;
348+ self
349+ }
350+
260351 // Prop
261352 //
262353
@@ -305,7 +396,8 @@ impl StdStreamSinkBuilder<StdStream> {
305396 pub fn build ( self ) -> Result < StdStreamSink > {
306397 Ok ( StdStreamSink {
307398 prop : self . prop ,
308- dest : StdStreamDest :: new ( self . std_stream ) ,
399+ via_print_macro : self . via_print_macro ,
400+ std_stream : self . std_stream ,
309401 should_render_style : StdStreamSink :: should_render_style (
310402 self . style_mode ,
311403 self . std_stream ,
0 commit comments