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

The builder methods are uniformly available for Style, Color, and Painted, which means you can chain calls across library types. They are also const, allowing creations of const or static styles which can be directly applied via the paint() method, also from the Paint trait and thus available for every type:

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

// `const` constructors allow static `Style` 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.

Stying can also be globally enabled conditionally and dynamically via whenever() based on an arbitrary Condition: a function that returns true to enable styling and false to disable it. The condition is evaluated every time a Painted items is displayed.

Per-Style

A specific Style can also 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

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 wrapping style.

It is needed only in situations when styling opaque items or items that may already be styled, such as when implementing a generic logger. It exists to enable consistent styling across such items:

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 red() reset after “Stop” is not overwritten: > Hey! Stop and Go

Wrapping incurs a performance cost via 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 clear itself after being applied. In other words, the style lingers on beyond the value it’s applied to and until something else clears the respective styling.

The complement to lingering is force clearing via Quirk::Clear or the equivalent clear() constructor. Force clearing, as the name implies, forces a clear 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 cleared 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 cleared here
    "you".on_yellow().linger(),
    "today".blue()); // doesn't linger; styling is cleared

> 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 clear
    "E".clear());  // explicitly clear

> 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 95% 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 is 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 no-default-features = true, this crate is #[no_std].

Without any features enabled, all functionality except wrapping is available. To recover wrapping with #[no_std], set no-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 styling globally based on condition.