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 | /// ``` |
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 | |
238 | impl 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 |
249 | pub struct StyledValue<'a, T> { |
250 | style: Cow<'a, Style>, |
251 | value: T, |
252 | } |
253 | |
254 | impl<'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 | |
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 { |
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 | |