Crate yansi

source ·
Expand description

A dead simple ANSI terminal color painting library.

§Features

Why yet another ANSI terminal coloring library? Here are some reasons:

All that said, yansi borrows API ideas from older libraries as well as implementation details from ansi_term.

§Usage

The Paint trait is implemented for every type. Import it and call chainable builder methods:

use yansi::Paint;

println!("Testing, {}, {}, {}!",
    "Ready".bold(),
    "Set".yellow().italic(),
    "STOP".white().on_red().bright().underline().bold());

> Testing, Ready, Set, STOP!

The methods return a Painted type which consists of a Style and a reference to the receiver. Displaying a Painted (via print!(), format!(), etc) results in emitting ANSI escape codes that effectuate the style.

§Uniform const Builders

All builder methods are uniformly available for Style, Color, and Painted, which means you can chain calls across library types. All methods are const, allowing creations of const or static Styles. A Style can be directly applied to values with .paint(), from Paint::paint(), available for every type:

use yansi::{Paint, Style, Color::*};

// `const` constructors allow static `Style`s for easy reuse
static ALERT: Style = White.bright().underline().italic().on_red();

println!("Testing, {}, {}, {}!",
    "Ready".bold(),
    "Set".yellow().bold(),
    "STOP".paint(ALERT));

> Testing, Ready, Set, STOP!

§Conditional Styling

§Globally

Styling is enabled by default but can be enabled and disabled globally via enable() and disable(). When styling is disabled, no ANSI escape codes are emitted, and masked values are omitted entirely.

Global styling can also be dynamically enabled and disabled using whenever() with an arbitrary Condition: a function that returns true or false. This condition is evaluated each time a Painted item is displayed. The associated styling is enabled, and mask values emitted, exactly when and only when the condition returns true.

§Per-Style

A specific Style can itself be conditionally applied by using .whenever():

use yansi::{Paint, Style, Color::*, Condition};

static WARNING: Style = Black.bold().on_yellow().whenever(Condition::STDERR_IS_TTY);

eprintln!("{}", "Bees can sting!".paint(WARNING));

With the above, if stderr is a TTY, then: > Bees can sting!

If it is not a TTY, styling is not emitted: > Bees can sting!

See Condition for a list of built-in conditions which require enabling crate features.

§Quirks

As a convenience, yansi implements several “quirks”, applicable via Quirk and the respective methods, that modify if and how styling is presented to the terminal. These quirks do not correspond to any ANSI styling sequences.

§Masking

Items can be arbitrarily masked with the mask() builder method. Masked values are not emitted when styling is disabled, globally or for a given style. This allows selective output based on whether styling is enabled.

One use for this feature is to print certain characters only when styling is enabled. For instance, you might wish to emit the 🎨 emoji when coloring is enabled but not otherwise. This can be accomplished by masking the emoji:

use yansi::Paint;

println!("I like colors!{}", " 🎨".mask());

When styling is enabled, this prints: > I like colors! 🎨

With styling disabled, this prints: > I like colors!

§Wrapping

Note: Either the std or alloc feature is required for wrapping. std is enabled by default. See crate features.

Styling can wrap via Quirk::Wrap or the equivalent wrap() constructor. A wrapping style modifies any styling resets emitted by the internal value so that they correspond to the wrapping style. In other words, the “reset” style of the wrapped item is modified to be the style being .wrap()d.

Wrapping is useful in situations where opaque and arbitrary values must be styled consistently irrespective of any existing styling. For example, a generic logger might want to style messages based on log levels consistently, even when those messages may already include styling. Wrapping exists to enable such consistent styling:

use yansi::Paint;

// Imagine that `inner` is opaque and we don't know it's styling.
let inner = format!("{} and {}", "Stop".red(), "Go".green());

// We can use `wrap` to ensure anything in `inner` not styled is blue.
println!("Hey! {}", inner.blue().wrap());

Thanks to wrapping, this prints: > Hey! Stop and Go

Without wrapping, the reset after "Stop".red() would not be overwritten: > Hey! Stop and Go

Wrapping incurs a performance cost due to an extra allocation and replacement if the wrapped item has styling applied to it. Otherwise, it does not allocate nor incur a meaningful performance cost.

§Lingering

Styling can linger beyond a single value via Quirk::Linger or the equivalent linger() constructor. A lingering style does not reset itself after being applied. In other words, the style lingers on beyond the value it’s applied to, until something else resets the respective styling.

The complement to lingering is force resetting via Quirk::Resetting or the equivalent resetting() constructor. Force resetting, as the name implies, forces a reset suffix to be emitted after the value, irrespective of any lingering applied. It can be used as a way to finalize a lingering style.

Lingering itself is useful in situations where a given style is to be repeated across multiple values, or when style is intended to persist even across values that are not styled with yansi. It also allows avoiding unnecessarily repeated ANSI code sequences. The examples below illustrate some scenarios in which lingering is useful:

use yansi::Paint;

println!("Hello! {} {} things with {} {}?",
    "How".magenta().underline().linger(),
    "are".italic().linger(),
    "you".on_yellow(), // doesn't linger, so all styling is reset here
    "today".blue());

> Hello! How are things with you today?

use yansi::Paint;

println!("Hello! {} {} things with {} {}?",
    "How".magenta().underline().linger(),
    "are".italic(), // doesn't linger, so all styling is reset here
    "you".on_yellow().linger(),
    "today".blue()); // doesn't linger; styling is reset

> Hello! How are things with you today?

use yansi::Paint;

println!("{} B {} {} {} F",
    "A".red().linger(),
    "C".underline().linger(),
    "D", // doesn't linger, but no styling applied, thus no reset
    "E".resetting());  // explicitly reset

> A B C D E F

§Brightening

Most pimrary colors are available in regular and bright variants, e.g., Color::Red and Color::BrightRed. The Quirk::Bright and Quirk::OnBright quirks, typically applied via .bright() and .on_bright(), provide an alternative, convenient mechanism to select the bright variant of the selected foreground or background color, respectively. The quirk provides no additional colors and is equivalent to selecting the bright variants directly.

use yansi::Paint;

// These are all equivalent.
print!("{}", "Regular".red());
print!("{}", "Bright".bright_red());
print!("{}", "Bright".bright().red());
print!("{}", "Bright".red().bright());

// The `bright` quirk lets use choose the bright variants of _any_ color,
// even when the color or style is unknown at the call site.
print!("{}", "Normal".paint(STYLE));
print!("{}", "Bright".paint(STYLE).bright());

> Regular Bright Bright Bright Normal Bright

The bright() quirk can be applied before or after a color is selected while having the same effect.

§Windows

Styling is supported and enabled automatically on Windows beginning with the Windows 10 Anniversary Update, or about 96% of all Windows machines worldwide, and likely closer to 100% of developer machines (e.g., 99% of visitors to rocket.rs on Windows are on Windows 10+).

Yansi enables styling support on Windows by querying the Windows API on the first attempt to color. If support is available, it is enabled. If support is not available, styling is disabled and no styling sequences are emitted.

§Crate Features

FeatureDefault?Also EnablesNotes
stdYallocUse std library.
allocYUse alloc. Enables wrapping.
detect-ttyNstdSee optional conditions.
detect-envNstdSee optional conditions.
hyperlinkNstdEnables hyperlinking support.

With default-features = false, this crate is #[no_std].

Without any features enabled, all functionality except wrapping is available. To recover wrapping with #[no_std], set default-features = false and enable the alloc feature, which requires alloc support.

Structs§

  • A function that decides whether styling should be applied.
  • An arbitrary value with a Style applied to it.
  • A set of styling options.

Enums§

  • Enum representing text attributes, largely for text formatting.
  • Enum representing a terminal color.
  • Enum representing a yansi quirk.

Traits§

  • A trait to apply styling to any value. Implemented for all types.

Functions§

  • Unconditionally disables styling globally.
  • Unconditionally enables styling globally.
  • Returns true if styling is globally enabled and false otherwise.
  • Dynamically enables and disables styling globally based on condition.