| use std::fmt; |
| |
| use style::{Style, Property}; |
| use color::Color; |
| |
| /// A structure encapsulating an item and styling. |
| /// |
| /// See the [crate level documentation](./) for usage information. |
| /// |
| /// # Method Glossary |
| /// |
| /// The `Paint` structure exposes many methods for convenience. |
| /// |
| /// ### Unstyled Constructors |
| /// |
| /// Return a new `Paint` structure with no or default styling applied. |
| /// |
| /// * [`Paint::new(item: T)`](Paint::new()) |
| /// * [`Paint::default(item: T)`](Paint::default()) |
| /// * [`Paint::masked(item: T)`](Paint::masked()) |
| /// * [`Paint::wrapping(item: T)`](Paint::wrapping()) |
| /// |
| /// ### Foreground Color Constructors |
| /// |
| /// Return a new `Paint` structure with a foreground color applied. |
| /// |
| /// * [`Paint::rgb(r: u8, g: u8, b: u8, item: T)`](Paint::rgb()) |
| /// * [`Paint::fixed(color: u8, item: T)`](Paint::fixed()) |
| /// * [`Paint::black(item: T)`](Paint::black()) |
| /// * [`Paint::red(item: T)`](Paint::red()) |
| /// * [`Paint::green(item: T)`](Paint::green()) |
| /// * [`Paint::yellow(item: T)`](Paint::yellow()) |
| /// * [`Paint::blue(item: T)`](Paint::blue()) |
| /// * [`Paint::magenta(item: T)`](Paint::magenta()) |
| /// * [`Paint::cyan(item: T)`](Paint::cyan()) |
| /// * [`Paint::white(item: T)`](Paint::white()) |
| /// |
| /// ### Getters |
| /// |
| /// Return information about the `Paint` structure. |
| /// |
| /// * [`paint.style()`](Paint::style()) |
| /// * [`paint.inner()`](Paint::inner()) |
| /// |
| /// ### Setters |
| /// |
| /// Set a style property on a given `Paint` structure. |
| /// |
| /// * [`paint.with_style(style: Style)`](Paint::with_style()) |
| /// * [`paint.mask()`](Paint::mask()) |
| /// * [`paint.wrap()`](Paint::wrap()) |
| /// * [`paint.fg(color: Color)`](Paint::fg()) |
| /// * [`paint.bg(color: Color)`](Paint::bg()) |
| /// * [`paint.bold()`](Paint::bold()) |
| /// * [`paint.dimmed()`](Paint::dimmed()) |
| /// * [`paint.italic()`](Paint::italic()) |
| /// * [`paint.underline()`](Paint::underline()) |
| /// * [`paint.blink()`](Paint::blink()) |
| /// * [`paint.invert()`](Paint::invert()) |
| /// * [`paint.hidden()`](Paint::hidden()) |
| /// * [`paint.strikethrough()`](Paint::strikethrough()) |
| /// |
| /// These methods can be chained: |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// |
| /// Paint::new("hi").underline().invert().italic().dimmed().bold(); |
| /// ``` |
| /// |
| /// ### Global Methods |
| /// |
| /// Modify or observe the global behavior of painting. |
| /// |
| /// * [`Paint::enable()`](Paint::enable()) |
| /// * [`Paint::disable()`](Paint::disable()) |
| /// * [`Paint::is_enabled()`](Paint::is_enabled()) |
| /// * [`Paint::enable_windows_ascii()`](Paint::enable_windows_ascii()) |
| #[derive(Default, Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone)] |
| pub struct Paint<T> { |
| item: T, |
| style: Style, |
| } |
| |
| macro_rules! constructors_for { |
| ($T:ty, $($name:ident: $color:ident),*) => ($( |
| docify!([ |
| Constructs a new @code{Paint} structure encapsulating @code{item} with |
| the foreground color set to $name. |
| |
| @fence @rust |
| use yansi::Paint; @nl @nl |
| |
| @{r#"println!("This is going to be "#} @[$name] @{r#": {}", "#} |
| @[Paint::$name] @{r#"("yay!"));"#} |
| @fence |
| ]; |
| #[inline] |
| pub fn $name(item: $T) -> Paint<$T> { |
| Paint::new(item).fg(Color::$color) |
| } |
| );)*) |
| } |
| |
| impl<T> Paint<T> { |
| /// Constructs a new `Paint` structure encapsulating `item` with no set |
| /// styling. |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// |
| /// assert_eq!(Paint::new("hello!").to_string(), "hello!".to_string()); |
| /// ``` |
| #[inline] |
| pub fn new(item: T) -> Paint<T> { |
| Paint { item, style: Style::default() } |
| } |
| |
| /// Constructs a new `Paint` structure encapsulating `item` with the active |
| /// terminal's default foreground and background. |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// |
| /// println!("This is going to use {}!", Paint::default("default colors")); |
| /// ``` |
| #[inline] |
| pub fn default(item: T) -> Paint<T> { |
| Paint::new(item).fg(Color::Default).bg(Color::Default) |
| } |
| |
| /// Constructs a new _masked_ `Paint` structure encapsulating `item` with |
| /// no set styling. |
| /// |
| /// A masked `Paint` is not written out when painting is disabled during |
| /// `Display` or `Debug` invocations. When painting is enabled, masking has |
| /// no effect. |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// |
| /// // The emoji won't be printed when coloring is disabled. |
| /// println!("{}Sprout!", Paint::masked("🌱 ")); |
| /// ``` |
| #[inline] |
| pub fn masked(item: T) -> Paint<T> { |
| Paint::new(item).mask() |
| } |
| |
| /// Constructs a new _wrapping_ `Paint` structure encapsulating `item` with |
| /// default styling. |
| /// |
| /// A wrapping `Paint` converts all color resets written out by the internal |
| /// value to the styling of itself. This allows for seamless color wrapping |
| /// of other colored text. |
| /// |
| /// # Performance |
| /// |
| /// In order to wrap an internal value, the internal value must first be |
| /// written out to a local buffer and examined. As a result, displaying a |
| /// wrapped value is likely to result in a heap allocation and copy. |
| /// |
| /// # Example |
| /// |
| /// ```rust |
| /// use yansi::{Paint, Color}; |
| /// |
| /// let inner = format!("{} and {}", Paint::red("Stop"), Paint::green("Go")); |
| /// |
| /// // 'Hey!' will be unstyled, "Stop" will be red, "and" will be blue, and |
| /// // "Go" will be green. Without a wrapping `Paint`, "and" would be |
| /// // unstyled. |
| /// println!("Hey! {}", Paint::wrapping(inner).fg(Color::Blue)); |
| /// ``` |
| #[inline] |
| pub fn wrapping(item: T) -> Paint<T> { |
| Paint::new(item).wrap() |
| } |
| |
| /// Constructs a new `Paint` structure encapsulating `item` with the |
| /// foreground color set to the RGB color `r`, `g`, `b`. |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// |
| /// println!("This is going to be funky: {}", Paint::rgb(70, 130, 122, "hi!")); |
| /// ``` |
| #[inline] |
| pub fn rgb(r: u8, g: u8, b: u8, item: T) -> Paint<T> { |
| Paint::new(item).fg(Color::RGB(r, g, b)) |
| } |
| |
| /// Constructs a new `Paint` structure encapsulating `item` with the |
| /// foreground color set to the fixed 256-bit color `color`. |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// |
| /// println!("This is going to be funky: {}", Paint::fixed(100, "hi!")); |
| /// ``` |
| #[inline] |
| pub fn fixed(color: u8, item: T) -> Paint<T> { |
| Paint::new(item).fg(Color::Fixed(color)) |
| } |
| |
| constructors_for!(T, black: Black, red: Red, green: Green, yellow: Yellow, |
| blue: Blue, magenta: Magenta, cyan: Cyan, white: White); |
| |
| /// Retrieves the style currently set on `self`. |
| /// |
| /// ```rust |
| /// use yansi::{Style, Color, Paint}; |
| /// |
| /// let alert = Style::new(Color::Red).bold().underline(); |
| /// let painted = Paint::red("hi").bold().underline(); |
| /// |
| /// assert_eq!(alert, painted.style()); |
| /// ``` |
| #[inline] |
| pub fn style(&self) -> Style { |
| self.style |
| } |
| |
| /// Retrieves a borrow to the inner item. |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// |
| /// let x = Paint::red("Hello, world!"); |
| /// assert_eq!(*x.inner(), "Hello, world!"); |
| /// ``` |
| #[inline] |
| pub fn inner(&self) -> &T { |
| &self.item |
| } |
| |
| /// Sets the style of `self` to `style`. |
| /// |
| /// Any styling currently set on `self` is lost. Prefer to use the |
| /// [`style.paint()`](Style::paint()) method to create a `Paint` struct from |
| /// `Style`. |
| /// |
| /// ```rust |
| /// use yansi::{Paint, Color, Style}; |
| /// |
| /// let s = Style::new(Color::Red).bold().underline(); |
| /// |
| /// // Using this method. |
| /// println!("Alert: {}", Paint::new("This thing happened!").with_style(s)); |
| /// |
| /// // Using the `style.paint()` method. |
| /// println!("Alert: {}", s.paint("This thing happened!")); |
| /// ``` |
| #[inline] |
| pub fn with_style(mut self, style: Style) -> Paint<T> { |
| self.style = style; |
| self |
| } |
| |
| /// Masks `self`. |
| /// |
| /// A masked `Paint` is not written out when painting is disabled during |
| /// `Display` or `Debug` invocations. When painting is enabled, masking has |
| /// no effect. |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// |
| /// // "Whoops! " will only print when coloring is enabled. |
| /// println!("{}Something happened.", Paint::red("Whoops! ").mask()); |
| /// ``` |
| #[inline] |
| pub fn mask(mut self) -> Paint<T> { |
| self.style.masked = true; |
| self |
| } |
| |
| /// Makes `self` a _wrapping_ `Paint`. |
| /// |
| /// A wrapping `Paint` converts all color resets written out by the internal |
| /// value to the styling of itself. This allows for seamless color wrapping |
| /// of other colored text. |
| /// |
| /// # Performance |
| /// |
| /// In order to wrap an internal value, the internal value must first be |
| /// written out to a local buffer and examined. As a result, displaying a |
| /// wrapped value is likely to result in a heap allocation and copy. |
| /// |
| /// # Example |
| /// |
| /// ```rust |
| /// use yansi::{Paint, Color}; |
| /// |
| /// let inner = format!("{} and {}", Paint::red("Stop"), Paint::green("Go")); |
| /// |
| /// // 'Hey!' will be unstyled, "Stop" will be red, "and" will be blue, and |
| /// // "Go" will be green. Without a wrapping `Paint`, "and" would be |
| /// // unstyled. |
| /// println!("Hey! {}", Paint::blue(inner).wrap()); |
| /// ``` |
| #[inline] |
| pub fn wrap(mut self) -> Paint<T> { |
| self.style.wrap = true; |
| self |
| } |
| |
| /// Sets the foreground to `color`. |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// use yansi::Color::Red; |
| /// |
| /// println!("Red foreground: {}", Paint::new("hi!").fg(Red)); |
| /// ``` |
| #[inline] |
| pub fn fg(mut self, color: Color) -> Paint<T> { |
| self.style.foreground = color; |
| self |
| } |
| |
| /// Sets the background to `color`. |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// use yansi::Color::Yellow; |
| /// |
| /// println!("Yellow background: {}", Paint::new("hi!").bg(Yellow)); |
| /// ``` |
| #[inline] |
| pub fn bg(mut self, color: Color) -> Paint<T> { |
| self.style.background = color; |
| self |
| } |
| |
| style_builder_for!(Paint<T>, |paint| paint.style.properties, |
| bold: BOLD, dimmed: DIMMED, italic: ITALIC, |
| underline: UNDERLINE, blink: BLINK, invert: INVERT, |
| hidden: HIDDEN, strikethrough: STRIKETHROUGH); |
| } |
| |
| macro_rules! impl_fmt_trait { |
| ($trait:ident, $fmt:expr) => ( |
| impl<T: fmt::$trait> fmt::$trait for Paint<T> { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| if Paint::is_enabled() && self.style.wrap { |
| let mut prefix = String::new(); |
| prefix.push_str("\x1B[0m"); |
| self.style.fmt_prefix(&mut prefix)?; |
| |
| self.style.fmt_prefix(f)?; |
| let item = format!($fmt, self.item).replace("\x1B[0m", &prefix); |
| fmt::$trait::fmt(&item, f)?; |
| self.style.fmt_suffix(f) |
| } else if Paint::is_enabled() { |
| self.style.fmt_prefix(f)?; |
| fmt::$trait::fmt(&self.item, f)?; |
| self.style.fmt_suffix(f) |
| } else if !self.style.masked { |
| fmt::$trait::fmt(&self.item, f) |
| } else { |
| Ok(()) |
| } |
| } |
| } |
| ) |
| } |
| |
| impl_fmt_trait!(Display, "{}"); |
| impl_fmt_trait!(Debug, "{:?}"); |
| |
| use std::sync::atomic::AtomicBool; |
| use std::sync::atomic::Ordering; |
| |
| static ENABLED: AtomicBool = AtomicBool::new(true); |
| |
| impl Paint<()> { |
| /// Disables coloring globally. |
| /// |
| /// # Example |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// |
| /// // With coloring enabled, ANSI color codes are emitted. |
| /// assert_ne!(Paint::green("go").to_string(), "go".to_string()); |
| /// |
| /// // With coloring disabled, ANSI color codes are _not_ emitted. |
| /// Paint::disable(); |
| /// assert_eq!(Paint::green("go").to_string(), "go".to_string()); |
| /// ``` |
| pub fn disable() { |
| ENABLED.store(false, Ordering::Release); |
| } |
| |
| /// Enables coloring globally. Coloring is enabled by default, so this |
| /// method should only be called to _re_ enable coloring. |
| /// |
| /// # Example |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// |
| /// // With coloring disabled, ANSI color codes are _not_ emitted. |
| /// Paint::disable(); |
| /// assert_eq!(Paint::green("go").to_string(), "go".to_string()); |
| /// |
| /// // Reenabling causes color code to be emitted. |
| /// Paint::enable(); |
| /// assert_ne!(Paint::green("go").to_string(), "go".to_string()); |
| /// ``` |
| pub fn enable() { |
| ENABLED.store(true, Ordering::Release); |
| } |
| |
| /// Returns `true` if coloring is enabled and `false` otherwise. Coloring is |
| /// enabled by default but can be enabled and disabled on-the-fly with the |
| /// [`Paint::enable()`] and [`Paint::disable()`] methods. |
| /// |
| /// [`Paint::disable()`]: struct.Paint.html#method.disable |
| /// [`Paint::enable()`]: struct.Paint.html#method.disable |
| /// |
| /// # Example |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// |
| /// // Coloring is enabled by default. |
| /// assert!(Paint::is_enabled()); |
| /// |
| /// // Disable it with `Paint::disable()`. |
| /// Paint::disable(); |
| /// assert!(!Paint::is_enabled()); |
| /// |
| /// // Reenable with `Paint::enable()`. |
| /// Paint::enable(); |
| /// assert!(Paint::is_enabled()); |
| /// ``` |
| pub fn is_enabled() -> bool { |
| ENABLED.load(Ordering::Acquire) |
| } |
| |
| /// Enables ASCII terminal escape sequences on Windows consoles when |
| /// possible. Returns `true` if escape sequence support was successfully |
| /// enabled and `false` otherwise. On non-Windows targets, this method |
| /// always returns `true`. |
| /// |
| /// Support for escape sequences in Windows consoles was added in the |
| /// Windows 10 anniversary update. For targets with older Windows |
| /// installations, this method is expected to return `false`. |
| /// |
| /// # Example |
| /// |
| /// ```rust |
| /// use yansi::Paint; |
| /// |
| /// // A best-effort Windows ASCII terminal support enabling. |
| /// Paint::enable_windows_ascii(); |
| /// ``` |
| #[inline] |
| pub fn enable_windows_ascii() -> bool { |
| ::windows::enable_ascii_colors() |
| } |
| } |