Skip to main content

env_logger/writer/
mod.rs

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