blob: aaef739f9eaa11c0b2b98e7a1801acee2462c2b4 [file] [log] [blame] [edit]
/// A style is a collection of properties that can format a string
/// using ANSI escape codes.
///
/// # Examples
///
/// ```
/// use yansi_term::{Style, Colour};
///
/// let style = Style::new().bold().on(Colour::Black);
/// println!("{}", style.paint("Bold on black"));
/// ```
#[derive(PartialEq, Clone, Copy, Default, Debug)]
#[cfg_attr(
feature = "derive_serde_style",
derive(serde::Deserialize, serde::Serialize)
)]
pub struct Style {
/// The style's foreground colour, if it has one.
pub foreground: Option<Colour>,
/// The style's background colour, if it has one.
pub background: Option<Colour>,
/// Whether this style is bold.
pub is_bold: bool,
/// Whether this style is dimmed.
pub is_dimmed: bool,
/// Whether this style is italic.
pub is_italic: bool,
/// Whether this style is underlined.
pub is_underline: bool,
/// Whether this style is blinking.
pub is_blink: bool,
/// Whether this style has reverse colours.
pub is_reverse: bool,
/// Whether this style is hidden.
pub is_hidden: bool,
/// Whether this style is struckthrough.
pub is_strikethrough: bool,
}
impl Style {
/// Creates a new Style with no properties set.
///
/// # Examples
///
/// ```
/// use yansi_term::Style;
///
/// let style = Style::new();
/// println!("{}", style.paint("hi"));
/// ```
pub fn new() -> Style {
Style::default()
}
/// Returns a `Style` with the bold property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Style;
///
/// let style = Style::new().bold();
/// println!("{}", style.paint("hey"));
/// ```
pub fn bold(mut self) -> Self {
self.is_bold = true;
self
}
/// Returns a `Style` with the dimmed property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Style;
///
/// let style = Style::new().dimmed();
/// println!("{}", style.paint("sup"));
/// ```
pub fn dimmed(mut self) -> Self {
self.is_dimmed = true;
self
}
/// Returns a `Style` with the italic property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Style;
///
/// let style = Style::new().italic();
/// println!("{}", style.paint("greetings"));
/// ```
pub fn italic(mut self) -> Self {
self.is_italic = true;
self
}
/// Returns a `Style` with the underline property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Style;
///
/// let style = Style::new().underline();
/// println!("{}", style.paint("salutations"));
/// ```
pub fn underline(mut self) -> Self {
self.is_underline = true;
self
}
/// Returns a `Style` with the blink property set.
/// # Examples
///
/// ```
/// use yansi_term::Style;
///
/// let style = Style::new().blink();
/// println!("{}", style.paint("wazzup"));
/// ```
pub fn blink(mut self) -> Self {
self.is_blink = true;
self
}
/// Returns a `Style` with the reverse property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Style;
///
/// let style = Style::new().reverse();
/// println!("{}", style.paint("aloha"));
/// ```
pub fn reverse(mut self) -> Self {
self.is_reverse = true;
self
}
/// Returns a `Style` with the hidden property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Style;
///
/// let style = Style::new().hidden();
/// println!("{}", style.paint("ahoy"));
/// ```
pub fn hidden(mut self) -> Self {
self.is_hidden = true;
self
}
/// Returns a `Style` with the strikethrough property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Style;
///
/// let style = Style::new().strikethrough();
/// println!("{}", style.paint("yo"));
/// ```
pub fn strikethrough(mut self) -> Self {
self.is_strikethrough = true;
self
}
/// Returns a `Style` with the foreground colour property set.
///
/// # Examples
///
/// ```
/// use yansi_term::{Style, Colour};
///
/// let style = Style::new().fg(Colour::Yellow);
/// println!("{}", style.paint("hi"));
/// ```
pub fn fg(mut self, foreground: Colour) -> Self {
self.foreground = Some(foreground);
self
}
/// Returns a `Style` with the background colour property set.
///
/// # Examples
///
/// ```
/// use yansi_term::{Style, Colour};
///
/// let style = Style::new().on(Colour::Blue);
/// println!("{}", style.paint("eyyyy"));
/// ```
pub fn on(mut self, background: Colour) -> Self {
self.background = Some(background);
self
}
/// Return true if this `Style` has no actual styles, and can be written
/// without any control characters.
///
/// # Examples
///
/// ```
/// use yansi_term::Style;
///
/// assert_eq!(true, Style::default().is_plain());
/// assert_eq!(false, Style::default().bold().is_plain());
/// ```
pub fn is_plain(&self) -> bool {
*self == Style::default()
}
}
/// A colour is one specific type of ANSI escape code, and can refer
/// to either the foreground or background colour.
///
/// These use the standard numeric sequences.
/// See <http://invisible-island.net/xterm/ctlseqs/ctlseqs.html>
#[derive(PartialEq, Clone, Copy, Debug)]
#[cfg_attr(
feature = "derive_serde_style",
derive(serde::Deserialize, serde::Serialize)
)]
pub enum Colour {
/// Colour #0 (foreground code `30`, background code `40`).
///
/// This is not necessarily the background colour, and using it as one may
/// render the text hard to read on terminals with dark backgrounds.
Black,
/// Colour #1 (foreground code `31`, background code `41`).
Red,
/// Colour #2 (foreground code `32`, background code `42`).
Green,
/// Colour #3 (foreground code `33`, background code `43`).
Yellow,
/// Colour #4 (foreground code `34`, background code `44`).
Blue,
/// Colour #5 (foreground code `35`, background code `45`).
Purple,
/// Colour #6 (foreground code `36`, background code `46`).
Cyan,
/// Colour #7 (foreground code `37`, background code `47`).
///
/// As above, this is not necessarily the foreground colour, and may be
/// hard to read on terminals with light backgrounds.
White,
/// A colour number from 0 to 255, for use in 256-colour terminal
/// environments.
///
/// - Colours 0 to 7 are the `Black` to `White` variants respectively.
/// These colours can usually be changed in the terminal emulator.
/// - Colours 8 to 15 are brighter versions of the eight colours above.
/// These can also usually be changed in the terminal emulator, or it
/// could be configured to use the original colours and show the text in
/// bold instead. It varies depending on the program.
/// - Colours 16 to 231 contain several palettes of bright colours,
/// arranged in six squares measuring six by six each.
/// - Colours 232 to 255 are shades of grey from black to white.
///
/// It might make more sense to look at a [colour chart][cc].
///
/// [cc]: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg
Fixed(u8),
/// A 24-bit RGB color, as specified by ISO-8613-3.
RGB(u8, u8, u8),
}
impl Colour {
/// Returns a `Style` with the foreground colour set to this colour.
///
/// # Examples
///
/// ```
/// use yansi_term::Colour;
///
/// let style = Colour::Red.normal();
/// println!("{}", style.paint("hi"));
/// ```
pub fn normal(self) -> Style {
Style {
foreground: Some(self),
..Style::default()
}
}
/// Returns a `Style` with the foreground colour set to this colour and the
/// bold property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Colour;
///
/// let style = Colour::Green.bold();
/// println!("{}", style.paint("hey"));
/// ```
pub fn bold(self) -> Style {
Style {
foreground: Some(self),
is_bold: true,
..Style::default()
}
}
/// Returns a `Style` with the foreground colour set to this colour and the
/// dimmed property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Colour;
///
/// let style = Colour::Yellow.dimmed();
/// println!("{}", style.paint("sup"));
/// ```
pub fn dimmed(self) -> Style {
Style {
foreground: Some(self),
is_dimmed: true,
..Style::default()
}
}
/// Returns a `Style` with the foreground colour set to this colour and the
/// italic property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Colour;
///
/// let style = Colour::Blue.italic();
/// println!("{}", style.paint("greetings"));
/// ```
pub fn italic(self) -> Style {
Style {
foreground: Some(self),
is_italic: true,
..Style::default()
}
}
/// Returns a `Style` with the foreground colour set to this colour and the
/// underline property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Colour;
///
/// let style = Colour::Purple.underline();
/// println!("{}", style.paint("salutations"));
/// ```
pub fn underline(self) -> Style {
Style {
foreground: Some(self),
is_underline: true,
..Style::default()
}
}
/// Returns a `Style` with the foreground colour set to this colour and the
/// blink property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Colour;
///
/// let style = Colour::Cyan.blink();
/// println!("{}", style.paint("wazzup"));
/// ```
pub fn blink(self) -> Style {
Style {
foreground: Some(self),
is_blink: true,
..Style::default()
}
}
/// Returns a `Style` with the foreground colour set to this colour and the
/// reverse property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Colour;
///
/// let style = Colour::Black.reverse();
/// println!("{}", style.paint("aloha"));
/// ```
pub fn reverse(self) -> Style {
Style {
foreground: Some(self),
is_reverse: true,
..Style::default()
}
}
/// Returns a `Style` with the foreground colour set to this colour and the
/// hidden property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Colour;
///
/// let style = Colour::White.hidden();
/// println!("{}", style.paint("ahoy"));
/// ```
pub fn hidden(self) -> Style {
Style {
foreground: Some(self),
is_hidden: true,
..Style::default()
}
}
/// Returns a `Style` with the foreground colour set to this colour and the
/// strikethrough property set.
///
/// # Examples
///
/// ```
/// use yansi_term::Colour;
///
/// let style = Colour::Fixed(244).strikethrough();
/// println!("{}", style.paint("yo"));
/// ```
pub fn strikethrough(self) -> Style {
Style {
foreground: Some(self),
is_strikethrough: true,
..Style::default()
}
}
/// Returns a `Style` with the foreground colour set to this colour and the
/// background colour property set to the given colour.
///
/// # Examples
///
/// ```
/// use yansi_term::Colour;
///
/// let style = Colour::RGB(31, 31, 31).on(Colour::White);
/// println!("{}", style.paint("eyyyy"));
/// ```
pub fn on(self, background: Colour) -> Style {
Style {
foreground: Some(self),
background: Some(background),
..Style::default()
}
}
}
impl From<Colour> for Style {
/// You can turn a `Colour` into a `Style` with the foreground colour set
/// with the `From` trait.
///
/// ```
/// use yansi_term::{Style, Colour};
/// let green_foreground = Style::default().fg(Colour::Green);
/// assert_eq!(green_foreground, Colour::Green.normal());
/// assert_eq!(green_foreground, Colour::Green.into());
/// assert_eq!(green_foreground, Style::from(Colour::Green));
/// ```
fn from(colour: Colour) -> Style {
colour.normal()
}
}
#[cfg(test)]
#[cfg(feature = "derive_serde_style")]
mod serde_json_tests {
use super::{Colour, Style};
#[test]
fn colour_serialization() {
let colours = &[
Colour::Red,
Colour::Blue,
Colour::RGB(123, 123, 123),
Colour::Fixed(255),
];
assert_eq!(
serde_json::to_string(&colours).unwrap(),
String::from("[\"Red\",\"Blue\",{\"RGB\":[123,123,123]},{\"Fixed\":255}]")
);
}
#[test]
fn colour_deserialization() {
let colours = &[
Colour::Red,
Colour::Blue,
Colour::RGB(123, 123, 123),
Colour::Fixed(255),
];
for colour in colours {
let serialized = serde_json::to_string(&colour).unwrap();
let deserialized: Colour = serde_json::from_str(&serialized).unwrap();
assert_eq!(colour, &deserialized);
}
}
#[test]
fn style_serialization() {
let style = Style::default();
assert_eq!(serde_json::to_string(&style).unwrap(), "{\"foreground\":null,\"background\":null,\"is_bold\":false,\"is_dimmed\":false,\"is_italic\":false,\"is_underline\":false,\"is_blink\":false,\"is_reverse\":false,\"is_hidden\":false,\"is_strikethrough\":false}".to_string());
}
}