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 { item: T, style: Style, } macro_rules! constructors_for { ($T:ty, $($name:ident: $color:ident),*) => ($( #[doc = concat!( "Constructs a new `Paint` structure encapsulating `item` with the foreground color\n", "set to ", stringify!($name), ".\n", "```rust\n", "use yansi::Paint;\n", "\n", "println!(\"This is going to be ", stringify!($name), ": {}\", Paint::", stringify!($name), "(\"yay!\"));\n", "```\n" )] #[inline] pub fn $name(item: $T) -> Paint<$T> { Paint::new(item).fg(Color::$color) } )*) } impl Paint { /// 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 { 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 { 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 { 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 { 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 { Paint::new(item).fg(Color::RGB(r, g, b)) } /// Constructs a new `Paint` structure encapsulating `item` with the /// foreground color set to the fixed 8-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 { 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 { 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 { 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 { 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 { 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 { self.style.background = color; self } style_builder_for!(Paint, |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 fmt::$trait for Paint { 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() } }