1 use std::borrow::Cow;
2 use std::cell::RefCell;
3 use std::fmt;
4 use std::rc::Rc;
5 
6 use super::Buffer;
7 
8 /// A set of styles to apply to the terminal output.
9 ///
10 /// Call [`Formatter::style`] to get a `Style` and use the builder methods to
11 /// set styling properties, like [color] and [weight].
12 /// To print a value using the style, wrap it in a call to [`value`] when the log
13 /// record is formatted.
14 ///
15 /// # Examples
16 ///
17 /// Create a bold, red colored style and use it to print the log level:
18 ///
19 /// ```
20 /// use std::io::Write;
21 /// use env_logger::fmt::Color;
22 ///
23 /// let mut builder = env_logger::Builder::new();
24 ///
25 /// builder.format(|buf, record| {
26 ///     let mut level_style = buf.style();
27 ///
28 ///     level_style.set_color(Color::Red).set_bold(true);
29 ///
30 ///     writeln!(buf, "{}: {}",
31 ///         level_style.value(record.level()),
32 ///         record.args())
33 /// });
34 /// ```
35 ///
36 /// Styles can be re-used to output multiple values:
37 ///
38 /// ```
39 /// use std::io::Write;
40 /// use env_logger::fmt::Color;
41 ///
42 /// let mut builder = env_logger::Builder::new();
43 ///
44 /// builder.format(|buf, record| {
45 ///     let mut bold = buf.style();
46 ///
47 ///     bold.set_bold(true);
48 ///
49 ///     writeln!(buf, "{}: {} {}",
50 ///         bold.value(record.level()),
51 ///         bold.value("some bold text"),
52 ///         record.args())
53 /// });
54 /// ```
55 ///
56 /// [`Formatter::style`]: struct.Formatter.html#method.style
57 /// [color]: #method.set_color
58 /// [weight]: #method.set_bold
59 /// [`value`]: #method.value
60 #[derive(Clone)]
61 pub struct Style {
62     pub(in crate::fmt) buf: Rc<RefCell<Buffer>>,
63     pub(in crate::fmt) spec: termcolor::ColorSpec,
64 }
65 
66 impl Style {
67     /// Set the text color.
68     ///
69     /// # Examples
70     ///
71     /// Create a style with red text:
72     ///
73     /// ```
74     /// use std::io::Write;
75     /// use env_logger::fmt::Color;
76     ///
77     /// let mut builder = env_logger::Builder::new();
78     ///
79     /// builder.format(|buf, record| {
80     ///     let mut style = buf.style();
81     ///
82     ///     style.set_color(Color::Red);
83     ///
84     ///     writeln!(buf, "{}", style.value(record.args()))
85     /// });
86     /// ```
set_color(&mut self, color: Color) -> &mut Style87     pub fn set_color(&mut self, color: Color) -> &mut Style {
88         self.spec.set_fg(Some(color.into_termcolor()));
89         self
90     }
91 
92     /// Set the text weight.
93     ///
94     /// If `yes` is true then text will be written in bold.
95     /// If `yes` is false then text will be written in the default weight.
96     ///
97     /// # Examples
98     ///
99     /// Create a style with bold text:
100     ///
101     /// ```
102     /// use std::io::Write;
103     ///
104     /// let mut builder = env_logger::Builder::new();
105     ///
106     /// builder.format(|buf, record| {
107     ///     let mut style = buf.style();
108     ///
109     ///     style.set_bold(true);
110     ///
111     ///     writeln!(buf, "{}", style.value(record.args()))
112     /// });
113     /// ```
set_bold(&mut self, yes: bool) -> &mut Style114     pub fn set_bold(&mut self, yes: bool) -> &mut Style {
115         self.spec.set_bold(yes);
116         self
117     }
118 
119     /// Set the text intensity.
120     ///
121     /// If `yes` is true then text will be written in a brighter color.
122     /// If `yes` is false then text will be written in the default color.
123     ///
124     /// # Examples
125     ///
126     /// Create a style with intense text:
127     ///
128     /// ```
129     /// use std::io::Write;
130     ///
131     /// let mut builder = env_logger::Builder::new();
132     ///
133     /// builder.format(|buf, record| {
134     ///     let mut style = buf.style();
135     ///
136     ///     style.set_intense(true);
137     ///
138     ///     writeln!(buf, "{}", style.value(record.args()))
139     /// });
140     /// ```
set_intense(&mut self, yes: bool) -> &mut Style141     pub fn set_intense(&mut self, yes: bool) -> &mut Style {
142         self.spec.set_intense(yes);
143         self
144     }
145 
146     /// Set whether the text is dimmed.
147     ///
148     /// If `yes` is true then text will be written in a dimmer color.
149     /// If `yes` is false then text will be written in the default color.
150     ///
151     /// # Examples
152     ///
153     /// Create a style with dimmed text:
154     ///
155     /// ```
156     /// use std::io::Write;
157     ///
158     /// let mut builder = env_logger::Builder::new();
159     ///
160     /// builder.format(|buf, record| {
161     ///     let mut style = buf.style();
162     ///
163     ///     style.set_dimmed(true);
164     ///
165     ///     writeln!(buf, "{}", style.value(record.args()))
166     /// });
167     /// ```
set_dimmed(&mut self, yes: bool) -> &mut Style168     pub fn set_dimmed(&mut self, yes: bool) -> &mut Style {
169         self.spec.set_dimmed(yes);
170         self
171     }
172 
173     /// Set the background color.
174     ///
175     /// # Examples
176     ///
177     /// Create a style with a yellow background:
178     ///
179     /// ```
180     /// use std::io::Write;
181     /// use env_logger::fmt::Color;
182     ///
183     /// let mut builder = env_logger::Builder::new();
184     ///
185     /// builder.format(|buf, record| {
186     ///     let mut style = buf.style();
187     ///
188     ///     style.set_bg(Color::Yellow);
189     ///
190     ///     writeln!(buf, "{}", style.value(record.args()))
191     /// });
192     /// ```
set_bg(&mut self, color: Color) -> &mut Style193     pub fn set_bg(&mut self, color: Color) -> &mut Style {
194         self.spec.set_bg(Some(color.into_termcolor()));
195         self
196     }
197 
198     /// Wrap a value in the style.
199     ///
200     /// The same `Style` can be used to print multiple different values.
201     ///
202     /// # Examples
203     ///
204     /// Create a bold, red colored style and use it to print the log level:
205     ///
206     /// ```
207     /// use std::io::Write;
208     /// use env_logger::fmt::Color;
209     ///
210     /// let mut builder = env_logger::Builder::new();
211     ///
212     /// builder.format(|buf, record| {
213     ///     let mut style = buf.style();
214     ///
215     ///     style.set_color(Color::Red).set_bold(true);
216     ///
217     ///     writeln!(buf, "{}: {}",
218     ///         style.value(record.level()),
219     ///         record.args())
220     /// });
221     /// ```
value<T>(&self, value: T) -> StyledValue<T>222     pub fn value<T>(&self, value: T) -> StyledValue<T> {
223         StyledValue {
224             style: Cow::Borrowed(self),
225             value,
226         }
227     }
228 
229     /// Wrap a value in the style by taking ownership of it.
into_value<T>(self, value: T) -> StyledValue<'static, T>230     pub(crate) fn into_value<T>(self, value: T) -> StyledValue<'static, T> {
231         StyledValue {
232             style: Cow::Owned(self),
233             value,
234         }
235     }
236 }
237 
238 impl fmt::Debug for Style {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result239     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
240         f.debug_struct("Style").field("spec", &self.spec).finish()
241     }
242 }
243 
244 /// A value that can be printed using the given styles.
245 ///
246 /// It is the result of calling [`Style::value`].
247 ///
248 /// [`Style::value`]: struct.Style.html#method.value
249 pub struct StyledValue<'a, T> {
250     style: Cow<'a, Style>,
251     value: T,
252 }
253 
254 impl<'a, T> StyledValue<'a, T> {
write_fmt<F>(&self, f: F) -> fmt::Result where F: FnOnce() -> fmt::Result,255     fn write_fmt<F>(&self, f: F) -> fmt::Result
256     where
257         F: FnOnce() -> fmt::Result,
258     {
259         self.style
260             .buf
261             .borrow_mut()
262             .set_color(&self.style.spec)
263             .map_err(|_| fmt::Error)?;
264 
265         // Always try to reset the terminal style, even if writing failed
266         let write = f();
267         let reset = self.style.buf.borrow_mut().reset().map_err(|_| fmt::Error);
268 
269         write.and(reset)
270     }
271 }
272 
273 macro_rules! impl_styled_value_fmt {
274     ($($fmt_trait:path),*) => {
275         $(
276             impl<'a, T: $fmt_trait> $fmt_trait for StyledValue<'a, T> {
277                 fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result {
278                     self.write_fmt(|| T::fmt(&self.value, f))
279                 }
280             }
281         )*
282     };
283 }
284 
285 impl_styled_value_fmt!(
286     fmt::Debug,
287     fmt::Display,
288     fmt::Pointer,
289     fmt::Octal,
290     fmt::Binary,
291     fmt::UpperHex,
292     fmt::LowerHex,
293     fmt::UpperExp,
294     fmt::LowerExp
295 );
296 
297 // The `Color` type is copied from https://github.com/BurntSushi/termcolor
298 
299 /// The set of available colors for the terminal foreground/background.
300 ///
301 /// The `Ansi256` and `Rgb` colors will only output the correct codes when
302 /// paired with the `Ansi` `WriteColor` implementation.
303 ///
304 /// The `Ansi256` and `Rgb` color types are not supported when writing colors
305 /// on Windows using the console. If they are used on Windows, then they are
306 /// silently ignored and no colors will be emitted.
307 ///
308 /// This set may expand over time.
309 ///
310 /// This type has a `FromStr` impl that can parse colors from their human
311 /// readable form. The format is as follows:
312 ///
313 /// 1. Any of the explicitly listed colors in English. They are matched
314 ///    case insensitively.
315 /// 2. A single 8-bit integer, in either decimal or hexadecimal format.
316 /// 3. A triple of 8-bit integers separated by a comma, where each integer is
317 ///    in decimal or hexadecimal format.
318 ///
319 /// Hexadecimal numbers are written with a `0x` prefix.
320 #[allow(missing_docs)]
321 #[non_exhaustive]
322 #[derive(Clone, Debug, Eq, PartialEq)]
323 pub enum Color {
324     Black,
325     Blue,
326     Green,
327     Red,
328     Cyan,
329     Magenta,
330     Yellow,
331     White,
332     Ansi256(u8),
333     Rgb(u8, u8, u8),
334 }
335 
336 impl Color {
into_termcolor(self) -> termcolor::Color337     fn into_termcolor(self) -> termcolor::Color {
338         match self {
339             Color::Black => termcolor::Color::Black,
340             Color::Blue => termcolor::Color::Blue,
341             Color::Green => termcolor::Color::Green,
342             Color::Red => termcolor::Color::Red,
343             Color::Cyan => termcolor::Color::Cyan,
344             Color::Magenta => termcolor::Color::Magenta,
345             Color::Yellow => termcolor::Color::Yellow,
346             Color::White => termcolor::Color::White,
347             Color::Ansi256(value) => termcolor::Color::Ansi256(value),
348             Color::Rgb(r, g, b) => termcolor::Color::Rgb(r, g, b),
349         }
350     }
351 }
352