env_logger/fmt/writer/
mod.rs1mod atty;
2mod termcolor;
3
4use self::atty::{is_stderr, is_stdout};
5use self::termcolor::BufferWriter;
6use std::{fmt, io, mem, sync::Mutex};
7
8pub(super) mod glob {
9    pub use super::termcolor::glob::*;
10    pub use super::*;
11}
12
13pub(super) use self::termcolor::Buffer;
14
15#[non_exhaustive]
17pub enum Target {
18    Stdout,
20    Stderr,
22    Pipe(Box<dyn io::Write + Send + 'static>),
24}
25
26impl Default for Target {
27    fn default() -> Self {
28        Target::Stderr
29    }
30}
31
32impl fmt::Debug for Target {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        write!(
35            f,
36            "{}",
37            match self {
38                Self::Stdout => "stdout",
39                Self::Stderr => "stderr",
40                Self::Pipe(_) => "pipe",
41            }
42        )
43    }
44}
45
46pub(super) enum WritableTarget {
50    Stdout,
52    Stderr,
54    Pipe(Box<Mutex<dyn io::Write + Send + 'static>>),
56}
57
58impl From<Target> for WritableTarget {
59    fn from(target: Target) -> Self {
60        match target {
61            Target::Stdout => Self::Stdout,
62            Target::Stderr => Self::Stderr,
63            Target::Pipe(pipe) => Self::Pipe(Box::new(Mutex::new(pipe))),
64        }
65    }
66}
67
68impl Default for WritableTarget {
69    fn default() -> Self {
70        Self::from(Target::default())
71    }
72}
73
74impl fmt::Debug for WritableTarget {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(
77            f,
78            "{}",
79            match self {
80                Self::Stdout => "stdout",
81                Self::Stderr => "stderr",
82                Self::Pipe(_) => "pipe",
83            }
84        )
85    }
86}
87#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
89pub enum WriteStyle {
90    Auto,
92    Always,
94    Never,
96}
97
98impl Default for WriteStyle {
99    fn default() -> Self {
100        WriteStyle::Auto
101    }
102}
103
104pub(crate) struct Writer {
106    inner: BufferWriter,
107    write_style: WriteStyle,
108}
109
110impl Writer {
111    pub fn write_style(&self) -> WriteStyle {
112        self.write_style
113    }
114
115    pub(super) fn buffer(&self) -> Buffer {
116        self.inner.buffer()
117    }
118
119    pub(super) fn print(&self, buf: &Buffer) -> io::Result<()> {
120        self.inner.print(buf)
121    }
122}
123
124#[derive(Debug)]
128pub(crate) struct Builder {
129    target: WritableTarget,
130    write_style: WriteStyle,
131    is_test: bool,
132    built: bool,
133}
134
135impl Builder {
136    pub(crate) fn new() -> Self {
138        Builder {
139            target: Default::default(),
140            write_style: Default::default(),
141            is_test: false,
142            built: false,
143        }
144    }
145
146    pub(crate) fn target(&mut self, target: Target) -> &mut Self {
148        self.target = target.into();
149        self
150    }
151
152    pub(crate) fn parse_write_style(&mut self, write_style: &str) -> &mut Self {
158        self.write_style(parse_write_style(write_style))
159    }
160
161    pub(crate) fn write_style(&mut self, write_style: WriteStyle) -> &mut Self {
163        self.write_style = write_style;
164        self
165    }
166
167    pub(crate) fn is_test(&mut self, is_test: bool) -> &mut Self {
169        self.is_test = is_test;
170        self
171    }
172
173    pub(crate) fn build(&mut self) -> Writer {
175        assert!(!self.built, "attempt to re-use consumed builder");
176        self.built = true;
177
178        let color_choice = match self.write_style {
179            WriteStyle::Auto => {
180                if match &self.target {
181                    WritableTarget::Stderr => is_stderr(),
182                    WritableTarget::Stdout => is_stdout(),
183                    WritableTarget::Pipe(_) => false,
184                } {
185                    WriteStyle::Auto
186                } else {
187                    WriteStyle::Never
188                }
189            }
190            color_choice => color_choice,
191        };
192
193        let writer = match mem::take(&mut self.target) {
194            WritableTarget::Stderr => BufferWriter::stderr(self.is_test, color_choice),
195            WritableTarget::Stdout => BufferWriter::stdout(self.is_test, color_choice),
196            WritableTarget::Pipe(pipe) => BufferWriter::pipe(self.is_test, color_choice, pipe),
197        };
198
199        Writer {
200            inner: writer,
201            write_style: self.write_style,
202        }
203    }
204}
205
206impl Default for Builder {
207    fn default() -> Self {
208        Builder::new()
209    }
210}
211
212impl fmt::Debug for Writer {
213    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214        f.debug_struct("Writer").finish()
215    }
216}
217
218fn parse_write_style(spec: &str) -> WriteStyle {
219    match spec {
220        "auto" => WriteStyle::Auto,
221        "always" => WriteStyle::Always,
222        "never" => WriteStyle::Never,
223        _ => Default::default(),
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use super::*;
230
231    #[test]
232    fn parse_write_style_valid() {
233        let inputs = vec![
234            ("auto", WriteStyle::Auto),
235            ("always", WriteStyle::Always),
236            ("never", WriteStyle::Never),
237        ];
238
239        for (input, expected) in inputs {
240            assert_eq!(expected, parse_write_style(input));
241        }
242    }
243
244    #[test]
245    fn parse_write_style_invalid() {
246        let inputs = vec!["", "true", "false", "NEVER!!"];
247
248        for input in inputs {
249            assert_eq!(WriteStyle::Auto, parse_write_style(input));
250        }
251    }
252}