env_logger/fmt/writer/
mod.rs

1mod atty;
2mod termcolor;
3
4use self::atty::{is_stderr, is_stdout};
5use self::termcolor::BufferWriter;
6use std::{fmt, io};
7
8pub(in crate::fmt) mod glob {
9    pub use super::termcolor::glob::*;
10    pub use super::*;
11}
12
13pub(in crate::fmt) use self::termcolor::Buffer;
14
15/// Log target, either `stdout` or `stderr`.
16#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
17pub enum Target {
18    /// Logs will be sent to standard output.
19    Stdout,
20    /// Logs will be sent to standard error.
21    Stderr,
22}
23
24impl Default for Target {
25    fn default() -> Self {
26        Target::Stderr
27    }
28}
29
30/// Whether or not to print styles to the target.
31#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
32pub enum WriteStyle {
33    /// Try to print styles, but don't force the issue.
34    Auto,
35    /// Try very hard to print styles.
36    Always,
37    /// Never print styles.
38    Never,
39}
40
41impl Default for WriteStyle {
42    fn default() -> Self {
43        WriteStyle::Auto
44    }
45}
46
47/// A terminal target with color awareness.
48pub(crate) struct Writer {
49    inner: BufferWriter,
50    write_style: WriteStyle,
51}
52
53impl Writer {
54    pub fn write_style(&self) -> WriteStyle {
55        self.write_style
56    }
57
58    pub(in crate::fmt) fn buffer(&self) -> Buffer {
59        self.inner.buffer()
60    }
61
62    pub(in crate::fmt) fn print(&self, buf: &Buffer) -> io::Result<()> {
63        self.inner.print(buf)
64    }
65}
66
67/// A builder for a terminal writer.
68///
69/// The target and style choice can be configured before building.
70pub(crate) struct Builder {
71    target: Target,
72    write_style: WriteStyle,
73    is_test: bool,
74    built: bool,
75}
76
77impl Builder {
78    /// Initialize the writer builder with defaults.
79    pub(crate) fn new() -> Self {
80        Builder {
81            target: Default::default(),
82            write_style: Default::default(),
83            is_test: false,
84            built: false,
85        }
86    }
87
88    /// Set the target to write to.
89    pub(crate) fn target(&mut self, target: Target) -> &mut Self {
90        self.target = target;
91        self
92    }
93
94    /// Parses a style choice string.
95    ///
96    /// See the [Disabling colors] section for more details.
97    ///
98    /// [Disabling colors]: ../index.html#disabling-colors
99    pub(crate) fn parse_write_style(&mut self, write_style: &str) -> &mut Self {
100        self.write_style(parse_write_style(write_style))
101    }
102
103    /// Whether or not to print style characters when writing.
104    pub(crate) fn write_style(&mut self, write_style: WriteStyle) -> &mut Self {
105        self.write_style = write_style;
106        self
107    }
108
109    /// Whether or not to capture logs for `cargo test`.
110    pub(crate) fn is_test(&mut self, is_test: bool) -> &mut Self {
111        self.is_test = is_test;
112        self
113    }
114
115    /// Build a terminal writer.
116    pub(crate) fn build(&mut self) -> Writer {
117        assert!(!self.built, "attempt to re-use consumed builder");
118        self.built = true;
119
120        let color_choice = match self.write_style {
121            WriteStyle::Auto => {
122                if match self.target {
123                    Target::Stderr => is_stderr(),
124                    Target::Stdout => is_stdout(),
125                } {
126                    WriteStyle::Auto
127                } else {
128                    WriteStyle::Never
129                }
130            }
131            color_choice => color_choice,
132        };
133
134        let writer = match self.target {
135            Target::Stderr => BufferWriter::stderr(self.is_test, color_choice),
136            Target::Stdout => BufferWriter::stdout(self.is_test, color_choice),
137        };
138
139        Writer {
140            inner: writer,
141            write_style: self.write_style,
142        }
143    }
144}
145
146impl Default for Builder {
147    fn default() -> Self {
148        Builder::new()
149    }
150}
151
152impl fmt::Debug for Builder {
153    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154        f.debug_struct("Logger")
155            .field("target", &self.target)
156            .field("write_style", &self.write_style)
157            .finish()
158    }
159}
160
161impl fmt::Debug for Writer {
162    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163        f.debug_struct("Writer").finish()
164    }
165}
166
167fn parse_write_style(spec: &str) -> WriteStyle {
168    match spec {
169        "auto" => WriteStyle::Auto,
170        "always" => WriteStyle::Always,
171        "never" => WriteStyle::Never,
172        _ => Default::default(),
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn parse_write_style_valid() {
182        let inputs = vec![
183            ("auto", WriteStyle::Auto),
184            ("always", WriteStyle::Always),
185            ("never", WriteStyle::Never),
186        ];
187
188        for (input, expected) in inputs {
189            assert_eq!(expected, parse_write_style(input));
190        }
191    }
192
193    #[test]
194    fn parse_write_style_invalid() {
195        let inputs = vec!["", "true", "false", "NEVER!!"];
196
197        for input in inputs {
198            assert_eq!(WriteStyle::Auto, parse_write_style(input));
199        }
200    }
201}