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