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, 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}