use std::borrow::Cow; use std::cell::RefCell; use std::fmt; use std::rc::Rc; use super::Buffer; /// A set of styles to apply to the terminal output. /// /// Call [`Formatter::style`] to get a `Style` and use the builder methods to /// set styling properties, like [color] and [weight]. /// To print a value using the style, wrap it in a call to [`value`] when the log /// record is formatted. /// /// # Examples /// /// Create a bold, red colored style and use it to print the log level: /// /// ``` /// use std::io::Write; /// use env_logger::fmt::Color; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut level_style = buf.style(); /// /// level_style.set_color(Color::Red).set_bold(true); /// /// writeln!(buf, "{}: {}", /// level_style.value(record.level()), /// record.args()) /// }); /// ``` /// /// Styles can be re-used to output multiple values: /// /// ``` /// use std::io::Write; /// use env_logger::fmt::Color; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut bold = buf.style(); /// /// bold.set_bold(true); /// /// writeln!(buf, "{}: {} {}", /// bold.value(record.level()), /// bold.value("some bold text"), /// record.args()) /// }); /// ``` /// /// [`Formatter::style`]: struct.Formatter.html#method.style /// [color]: #method.set_color /// [weight]: #method.set_bold /// [`value`]: #method.value #[derive(Clone)] pub struct Style { pub(in crate::fmt) buf: Rc>, pub(in crate::fmt) spec: termcolor::ColorSpec, } impl Style { /// Set the text color. /// /// # Examples /// /// Create a style with red text: /// /// ``` /// use std::io::Write; /// use env_logger::fmt::Color; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut style = buf.style(); /// /// style.set_color(Color::Red); /// /// writeln!(buf, "{}", style.value(record.args())) /// }); /// ``` pub fn set_color(&mut self, color: Color) -> &mut Style { self.spec.set_fg(Some(color.into_termcolor())); self } /// Set the text weight. /// /// If `yes` is true then text will be written in bold. /// If `yes` is false then text will be written in the default weight. /// /// # Examples /// /// Create a style with bold text: /// /// ``` /// use std::io::Write; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut style = buf.style(); /// /// style.set_bold(true); /// /// writeln!(buf, "{}", style.value(record.args())) /// }); /// ``` pub fn set_bold(&mut self, yes: bool) -> &mut Style { self.spec.set_bold(yes); self } /// Set the text intensity. /// /// If `yes` is true then text will be written in a brighter color. /// If `yes` is false then text will be written in the default color. /// /// # Examples /// /// Create a style with intense text: /// /// ``` /// use std::io::Write; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut style = buf.style(); /// /// style.set_intense(true); /// /// writeln!(buf, "{}", style.value(record.args())) /// }); /// ``` pub fn set_intense(&mut self, yes: bool) -> &mut Style { self.spec.set_intense(yes); self } /// Set whether the text is dimmed. /// /// If `yes` is true then text will be written in a dimmer color. /// If `yes` is false then text will be written in the default color. /// /// # Examples /// /// Create a style with dimmed text: /// /// ``` /// use std::io::Write; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut style = buf.style(); /// /// style.set_dimmed(true); /// /// writeln!(buf, "{}", style.value(record.args())) /// }); /// ``` pub fn set_dimmed(&mut self, yes: bool) -> &mut Style { self.spec.set_dimmed(yes); self } /// Set the background color. /// /// # Examples /// /// Create a style with a yellow background: /// /// ``` /// use std::io::Write; /// use env_logger::fmt::Color; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut style = buf.style(); /// /// style.set_bg(Color::Yellow); /// /// writeln!(buf, "{}", style.value(record.args())) /// }); /// ``` pub fn set_bg(&mut self, color: Color) -> &mut Style { self.spec.set_bg(Some(color.into_termcolor())); self } /// Wrap a value in the style. /// /// The same `Style` can be used to print multiple different values. /// /// # Examples /// /// Create a bold, red colored style and use it to print the log level: /// /// ``` /// use std::io::Write; /// use env_logger::fmt::Color; /// /// let mut builder = env_logger::Builder::new(); /// /// builder.format(|buf, record| { /// let mut style = buf.style(); /// /// style.set_color(Color::Red).set_bold(true); /// /// writeln!(buf, "{}: {}", /// style.value(record.level()), /// record.args()) /// }); /// ``` pub fn value(&self, value: T) -> StyledValue { StyledValue { style: Cow::Borrowed(self), value, } } /// Wrap a value in the style by taking ownership of it. pub(crate) fn into_value(self, value: T) -> StyledValue<'static, T> { StyledValue { style: Cow::Owned(self), value, } } } impl fmt::Debug for Style { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Style").field("spec", &self.spec).finish() } } /// A value that can be printed using the given styles. /// /// It is the result of calling [`Style::value`]. /// /// [`Style::value`]: struct.Style.html#method.value pub struct StyledValue<'a, T> { style: Cow<'a, Style>, value: T, } impl<'a, T> StyledValue<'a, T> { fn write_fmt(&self, f: F) -> fmt::Result where F: FnOnce() -> fmt::Result, { self.style .buf .borrow_mut() .set_color(&self.style.spec) .map_err(|_| fmt::Error)?; // Always try to reset the terminal style, even if writing failed let write = f(); let reset = self.style.buf.borrow_mut().reset().map_err(|_| fmt::Error); write.and(reset) } } macro_rules! impl_styled_value_fmt { ($($fmt_trait:path),*) => { $( impl<'a, T: $fmt_trait> $fmt_trait for StyledValue<'a, T> { fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result { self.write_fmt(|| T::fmt(&self.value, f)) } } )* }; } impl_styled_value_fmt!( fmt::Debug, fmt::Display, fmt::Pointer, fmt::Octal, fmt::Binary, fmt::UpperHex, fmt::LowerHex, fmt::UpperExp, fmt::LowerExp ); // The `Color` type is copied from https://github.com/BurntSushi/termcolor /// The set of available colors for the terminal foreground/background. /// /// The `Ansi256` and `Rgb` colors will only output the correct codes when /// paired with the `Ansi` `WriteColor` implementation. /// /// The `Ansi256` and `Rgb` color types are not supported when writing colors /// on Windows using the console. If they are used on Windows, then they are /// silently ignored and no colors will be emitted. /// /// This set may expand over time. /// /// This type has a `FromStr` impl that can parse colors from their human /// readable form. The format is as follows: /// /// 1. Any of the explicitly listed colors in English. They are matched /// case insensitively. /// 2. A single 8-bit integer, in either decimal or hexadecimal format. /// 3. A triple of 8-bit integers separated by a comma, where each integer is /// in decimal or hexadecimal format. /// /// Hexadecimal numbers are written with a `0x` prefix. #[allow(missing_docs)] #[non_exhaustive] #[derive(Clone, Debug, Eq, PartialEq)] pub enum Color { Black, Blue, Green, Red, Cyan, Magenta, Yellow, White, Ansi256(u8), Rgb(u8, u8, u8), } impl Color { fn into_termcolor(self) -> termcolor::Color { match self { Color::Black => termcolor::Color::Black, Color::Blue => termcolor::Color::Blue, Color::Green => termcolor::Color::Green, Color::Red => termcolor::Color::Red, Color::Cyan => termcolor::Color::Cyan, Color::Magenta => termcolor::Color::Magenta, Color::Yellow => termcolor::Color::Yellow, Color::White => termcolor::Color::White, Color::Ansi256(value) => termcolor::Color::Ansi256(value), Color::Rgb(r, g, b) => termcolor::Color::Rgb(r, g, b), } } }