1 mod atty;
2 mod termcolor;
3 
4 use self::atty::{is_stderr, is_stdout};
5 use self::termcolor::BufferWriter;
6 use std::{fmt, io};
7 
8 pub(in crate::fmt) mod glob {
9     pub use super::termcolor::glob::*;
10     pub use super::*;
11 }
12 
13 pub(in crate::fmt) use self::termcolor::Buffer;
14 
15 /// Log target, either `stdout` or `stderr`.
16 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
17 pub enum Target {
18     /// Logs will be sent to standard output.
19     Stdout,
20     /// Logs will be sent to standard error.
21     Stderr,
22 }
23 
24 impl Default for Target {
default() -> Self25     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)]
32 pub 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 
41 impl Default for WriteStyle {
default() -> Self42     fn default() -> Self {
43         WriteStyle::Auto
44     }
45 }
46 
47 /// A terminal target with color awareness.
48 pub(crate) struct Writer {
49     inner: BufferWriter,
50     write_style: WriteStyle,
51 }
52 
53 impl Writer {
write_style(&self) -> WriteStyle54     pub fn write_style(&self) -> WriteStyle {
55         self.write_style
56     }
57 
buffer(&self) -> Buffer58     pub(in crate::fmt) fn buffer(&self) -> Buffer {
59         self.inner.buffer()
60     }
61 
print(&self, buf: &Buffer) -> io::Result<()>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.
70 pub(crate) struct Builder {
71     target: Target,
72     write_style: WriteStyle,
73     is_test: bool,
74     built: bool,
75 }
76 
77 impl Builder {
78     /// Initialize the writer builder with defaults.
new() -> Self79     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.
target(&mut self, target: Target) -> &mut Self89     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
parse_write_style(&mut self, write_style: &str) -> &mut Self99     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.
write_style(&mut self, write_style: WriteStyle) -> &mut Self104     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`.
is_test(&mut self, is_test: bool) -> &mut Self110     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.
build(&mut self) -> Writer116     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 
146 impl Default for Builder {
default() -> Self147     fn default() -> Self {
148         Builder::new()
149     }
150 }
151 
152 impl fmt::Debug for Builder {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result153     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 
161 impl fmt::Debug for Writer {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result162     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163         f.debug_struct("Writer").finish()
164     }
165 }
166 
parse_write_style(spec: &str) -> WriteStyle167 fn 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)]
177 mod tests {
178     use super::*;
179 
180     #[test]
parse_write_style_valid()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]
parse_write_style_invalid()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 }
202