Expand description
A dead simple ANSI terminal color painting library.
Features
Why yet another ANSI terminal coloring library? Here are some reasons:
- This library makes simple things simple:
use
Paint
and go! - Zero dependencies by default. It really is simple.
- Zero allocations except as needed by opt-in wrapping.
- Automatic Windows support for the vast majority (95%+) of Windows users.
- Featureful
no_std
, no-alloc
, support withdefault-features = false
. Style
constructors areconst
: store styles statically, even with dynamic conditions!- Any type implementing a formatting trait can be styled, not just strings.
- Styling can be enabled and disabled globally and dynamically, on the fly.
- A
Style
can be predicated on arbitrary conditions. - Formatting specifiers like
{:x}
and{:08b}
are supported and preserved! - Built-in (optional) conditions for TTY detection and common environment variables.
- Arbitrary items can be masked for selective disabling.
- Styling can wrap to preserve styling across resets.
- Styling can linger beyond a single value.
- Experimental support for hyperlinking is included.
- The name
yansi
is pretty cool 😎.
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
Feature | Default? | Also Enables | Notes |
---|---|---|---|
std | Y | alloc | Use std library. |
alloc | Y | Use alloc . Enables wrapping. | |
detect-tty | N | std | See optional conditions. |
detect-env | N | std | See optional conditions. |
hyperlink | N | std | Enables 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 andfalse
otherwise. - Dynamically enables styling globally based on
condition
.