env_logger/fmt/writer/
buffer.rs

1use std::{io, sync::Mutex};
2
3use crate::fmt::writer::WriteStyle;
4
5#[derive(Debug)]
6pub(in crate::fmt::writer) struct BufferWriter {
7    target: WritableTarget,
8    write_style: WriteStyle,
9}
10
11impl BufferWriter {
12    pub(in crate::fmt::writer) fn stderr(is_test: bool, write_style: WriteStyle) -> Self {
13        BufferWriter {
14            target: if is_test {
15                WritableTarget::PrintStderr
16            } else {
17                WritableTarget::WriteStderr
18            },
19            write_style,
20        }
21    }
22
23    pub(in crate::fmt::writer) fn stdout(is_test: bool, write_style: WriteStyle) -> Self {
24        BufferWriter {
25            target: if is_test {
26                WritableTarget::PrintStdout
27            } else {
28                WritableTarget::WriteStdout
29            },
30            write_style,
31        }
32    }
33
34    pub(in crate::fmt::writer) fn pipe(
35        pipe: Box<Mutex<dyn io::Write + Send + 'static>>,
36        write_style: WriteStyle,
37    ) -> Self {
38        BufferWriter {
39            target: WritableTarget::Pipe(pipe),
40            write_style,
41        }
42    }
43
44    pub(in crate::fmt::writer) fn write_style(&self) -> WriteStyle {
45        self.write_style
46    }
47
48    pub(in crate::fmt::writer) fn buffer(&self) -> Buffer {
49        Buffer(Vec::new())
50    }
51
52    pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> {
53        #![allow(clippy::print_stdout)] // enabled for tests only
54        #![allow(clippy::print_stderr)] // enabled for tests only
55
56        use std::io::Write as _;
57
58        let buf = buf.as_bytes();
59        match &self.target {
60            WritableTarget::WriteStdout => {
61                let stream = io::stdout();
62                #[cfg(feature = "color")]
63                let stream = anstream::AutoStream::new(stream, self.write_style.into());
64                let mut stream = stream.lock();
65                stream.write_all(buf)?;
66                stream.flush()?;
67            }
68            WritableTarget::PrintStdout => {
69                #[cfg(feature = "color")]
70                let buf = adapt(buf, self.write_style)?;
71                #[cfg(feature = "color")]
72                let buf = &buf;
73                let buf = String::from_utf8_lossy(buf);
74                print!("{buf}");
75            }
76            WritableTarget::WriteStderr => {
77                let stream = io::stderr();
78                #[cfg(feature = "color")]
79                let stream = anstream::AutoStream::new(stream, self.write_style.into());
80                let mut stream = stream.lock();
81                stream.write_all(buf)?;
82                stream.flush()?;
83            }
84            WritableTarget::PrintStderr => {
85                #[cfg(feature = "color")]
86                let buf = adapt(buf, self.write_style)?;
87                #[cfg(feature = "color")]
88                let buf = &buf;
89                let buf = String::from_utf8_lossy(buf);
90                eprint!("{buf}");
91            }
92            WritableTarget::Pipe(pipe) => {
93                #[cfg(feature = "color")]
94                let buf = adapt(buf, self.write_style)?;
95                #[cfg(feature = "color")]
96                let buf = &buf;
97                let mut stream = pipe.lock().expect("no panics while held");
98                stream.write_all(buf)?;
99                stream.flush()?;
100            }
101        }
102
103        Ok(())
104    }
105}
106
107#[cfg(feature = "color")]
108fn adapt(buf: &[u8], write_style: WriteStyle) -> io::Result<Vec<u8>> {
109    use std::io::Write as _;
110
111    let adapted = Vec::with_capacity(buf.len());
112    let mut stream = anstream::AutoStream::new(adapted, write_style.into());
113    stream.write_all(buf)?;
114    let adapted = stream.into_inner();
115    Ok(adapted)
116}
117
118pub(in crate::fmt) struct Buffer(Vec<u8>);
119
120impl Buffer {
121    pub(in crate::fmt) fn clear(&mut self) {
122        self.0.clear();
123    }
124
125    pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
126        self.0.extend(buf);
127        Ok(buf.len())
128    }
129
130    pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
131        Ok(())
132    }
133
134    pub(in crate::fmt) fn as_bytes(&self) -> &[u8] {
135        &self.0
136    }
137}
138
139impl std::fmt::Debug for Buffer {
140    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141        String::from_utf8_lossy(self.as_bytes()).fmt(f)
142    }
143}
144
145/// Log target, either `stdout`, `stderr` or a custom pipe.
146///
147/// Same as `Target`, except the pipe is wrapped in a mutex for interior mutability.
148pub(super) enum WritableTarget {
149    /// Logs will be written to standard output.
150    WriteStdout,
151    /// Logs will be printed to standard output.
152    PrintStdout,
153    /// Logs will be written to standard error.
154    WriteStderr,
155    /// Logs will be printed to standard error.
156    PrintStderr,
157    /// Logs will be sent to a custom pipe.
158    Pipe(Box<Mutex<dyn io::Write + Send + 'static>>),
159}
160
161impl std::fmt::Debug for WritableTarget {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        write!(
164            f,
165            "{}",
166            match self {
167                Self::WriteStdout => "stdout",
168                Self::PrintStdout => "stdout",
169                Self::WriteStderr => "stderr",
170                Self::PrintStderr => "stderr",
171                Self::Pipe(_) => "pipe",
172            }
173        )
174    }
175}