blob: c5e66ef609c487db5c67d5b7217f7f705fd96b83 [file] [log] [blame] [edit]
//! A dead simple ANSI terminal color painting library.
//!
//! # Features
//!
//! Why *y*et 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](#wrapping).
//! * [Automatic Windows support] for the vast majority (95%+) of Windows
//! users.
//! * [Featureful `no_std`], no-`alloc`, support with `default-features =
//! false`.
//! * [`Style` constructors are `const`]: 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](#per-style).
//! * 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`].
//!
//! [`ansi_term`]: https://crates.io/crates/ansi_term
//! [`colored`]: https://crates.io/crates/colored
//! [`term_painter`]: https://crates.io/crates/term-painter
//! [_masked_]: #masking
//! [_wrap_]: #wrapping
//! [_linger_]: #lingering
//! [enabled]: crate::enable
//! [disabled]: crate::disable
//! [dynamically]: crate::whenever
//! [enabled conditionally]: Condition
//! [TTY detection]: Condition#impl-Condition-1
//! [common environment variables]: Condition#impl-Condition-2
//! [Automatic Windows support]: #windows
//! [Built-in (optional) conditions]: Condition#built-in-conditions
//! [`Style` constructors are `const`]: #uniform-const-builders
//! [hyperlinking]: hyperlink
//! [Featureful `no_std`]: #crate-features
//!
//! # Usage
//!
//! The [`Paint`] trait is implemented for every type. Import it and call
//! chainable builder methods:
//!
//! ```rust
//! use yansi::Paint;
//!
//! println!("Testing, {}, {}, {}!",
//! "Ready".bold(),
//! "Set".yellow().italic(),
//! "STOP".white().on_red().bright().underline().bold());
//! ```
//!
//! `>` Testing,
//! <b>Ready</b>,
//! <span style="color: yellow;"><i><b>Set</b></i></span>,
//! <span style="color: white; background: red;"><u><b>STOP</b></u></span>!
//!
//! 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` [`Style`]s. A
//! `Style` can be directly applied to values with [`.paint()`](Paint::paint()),
//! from [`Paint::paint()`], available for every type:
//!
//! ```rust
//! 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,
//! <b>Ready</b>,
//! <span style="color: yellow;"><b>Set</b></span>,
//! <span style="color: white; background: red;"><u><em>STOP</em></u></span>!
//!
//! ## 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()`](Style::whenever()):
//!
//! ```rust
//! # #[cfg(feature = "detect-tty")] {
//! 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:
//! `>` <span style="background: yellow; color: black;"><b>Bees can sting!</b></span>
//!
//! 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()`](Paint::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:
//!
//! ```rust
//! 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](#crate-features)._
//!
//! Styling can _wrap_ via [`Quirk::Wrap`] or the equivalent
//! [`wrap()`](Painted::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:
//!
//! ```rust
//! 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! <span style="color: blue">
//! <span style="color: red">Stop</span> and
//! <span style="color: green">Go</span>
//! </span>
//!
//! Without wrapping, the reset after `"Stop".red()` would not be overwritten:
//! `>` Hey! <span style="color: red">Stop</span> and <span style="color: green">Go</span>
//!
//! 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()`](Painted::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()`](Painted::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:
//!
//! ```rust
//! 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!
//! <span style="color: magenta;">
//! <u>How <i>are things with <span style="background: yellow;">you</span></i></u>
//! </span>
//! <span style="color: blue;">today</span>?
//!
//! ```rust
//! 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!
//! <span style="color: magenta;">
//! <u>How <i>are</i></u>
//! </span>
//! things with
//! <span style="background: yellow;">
//! you
//! <span style="color: blue;">today</span></span>?
//!
//! ```rust
//! 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
//! ```
//!
//! `>` <span style="color: red;"> A B <u>C D E</u> </span> 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()`](Painted::bright()) and [`.on_bright()`](Painted::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.
//!
//! ```rust
//! use yansi::Paint;
//!
//! // These are all equivalent.
//! print!("{}", "Regular".red());
//! print!("{}", "Bright".bright_red());
//! print!("{}", "Bright".bright().red());
//! print!("{}", "Bright".red().bright());
//!
//! # static STYLE: yansi::Style = yansi::Color::Green.bold();
//! // 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());
//! ```
//!
//! `>` <span style="color: red;">Regular</span>
//! <span style="color: hotpink;">Bright</span>
//! <span style="color: hotpink;">Bright</span>
//! <span style="color: hotpink;">Bright</span>
//! <span style="color: green;"><b>Normal</b></span>
//! <span style="color: greenyellow;"><b>Bright</b></span>
//!
//! 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](https://gs.statcounter.com/os-version-market-share/windows/desktop/worldwide),
//! and likely closer to 100% of developer machines (e.g., 99% of visitors to
//! [rocket.rs](https://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
//!
//! | 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 `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.
//!
//! [optional conditions]: Condition#built-in-conditions
//! [wrapping]: #wrapping
#![doc(html_logo_url = "https://raw.githubusercontent.com/SergioBenitez/yansi/master/.github/yansi-logo.png")]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "_nightly", feature(doc_cfg))]
#![deny(missing_docs)]
// FIXME: Remove once `clear()` and `Quirk::Clear` are removed.
#![allow(useless_deprecated, deprecated)]
#[cfg(all(not(feature = "std"), feature = "alloc"))]
extern crate alloc;
#[macro_use]
mod macros;
mod windows;
mod attr_quirk;
mod style;
mod color;
mod paint;
mod global;
mod condition;
mod set;
#[cfg(feature = "hyperlink")]
#[cfg_attr(feature = "_nightly", doc(cfg(feature = "hyperlink")))]
pub mod hyperlink;
pub use paint::{Painted, Paint};
pub use attr_quirk::{Attribute, Quirk};
pub use style::Style;
pub use color::Color;
pub use condition::Condition;
pub use global::{enable, whenever, disable, is_enabled};