1use std::borrow::Cow;
2use std::cell::RefCell;
3use std::fmt;
4use std::rc::Rc;
5
6use 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)]
61pub struct Style {
62 pub(in crate::fmt) buf: Rc<RefCell<Buffer>>,
63 pub(in crate::fmt) spec: termcolor::ColorSpec,
64}
65
66impl 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 /// ```
87 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 /// ```
114 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 /// ```
141 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 /// ```
168 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 /// ```
193 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 /// ```
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.
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
238impl fmt::Debug for Style {
239 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
240 f.debug_struct("Style").field(name:"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
249pub struct StyledValue<'a, T> {
250 style: Cow<'a, Style>,
251 value: T,
252}
253
254impl<'a, T> StyledValue<'a, T> {
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: Result<(), Error> = f();
267 let reset: Result<(), Error> = self.style.buf.borrow_mut().reset().map_err(|_| fmt::Error);
268
269 write.and(res:reset)
270 }
271}
272
273macro_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
285impl_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)]
323pub 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
336impl Color {
337 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: u8) => termcolor::Color::Ansi256(value),
348 Color::Rgb(r: u8, g: u8, b: u8) => termcolor::Color::Rgb(r, g, b),
349 }
350 }
351}
352