yansi/lib.rs
1//! A dead simple ANSI terminal color painting library.
2//!
3//! # Features
4//!
5//! Why *y*et another *ANSI* terminal coloring library? Here are some reasons:
6//!
7//! * This library makes simple things _simple_: `use` [`Paint`] and go!
8//! * Zero dependencies by default. It really is simple.
9//! * Zero allocations except as needed by opt-in [wrapping](#wrapping).
10//! * [Automatic Windows support] for the vast majority (95%+) of Windows
11//! users.
12//! * [Featureful `no_std`], no-`alloc`, support with `default-features =
13//! false`.
14//! * [`Style` constructors are `const`]: store styles statically, even with
15//! dynamic conditions!
16//! * _Any_ type implementing a formatting trait can be styled, not just
17//! strings.
18//! * Styling can be [enabled] and [disabled] globally and [dynamically], on
19//! the fly.
20//! * A `Style` can be predicated on arbitrary [conditions](#per-style).
21//! * Formatting specifiers like `{:x}` and `{:08b}` are supported and
22//! preserved!
23//! * [Built-in (optional) conditions] for [TTY detection] and [common
24//! environment variables].
25//! * Arbitrary items can be [_masked_] for selective disabling.
26//! * Styling can [_wrap_] to preserve styling across resets.
27//! * Styling can [_linger_] beyond a single value.
28//! * Experimental support for [hyperlinking] is included.
29//! * The name `yansi` is pretty cool 😎.
30//!
31//! All that said, `yansi` borrows API ideas from older libraries as well as
32//! implementation details from [`ansi_term`].
33//!
34//! [`ansi_term`]: https://crates.io/crates/ansi_term
35//! [`colored`]: https://crates.io/crates/colored
36//! [`term_painter`]: https://crates.io/crates/term-painter
37//! [_masked_]: #masking
38//! [_wrap_]: #wrapping
39//! [_linger_]: #lingering
40//! [enabled]: crate::enable
41//! [disabled]: crate::disable
42//! [dynamically]: crate::whenever
43//! [enabled conditionally]: Condition
44//! [TTY detection]: Condition#impl-Condition-1
45//! [common environment variables]: Condition#impl-Condition-2
46//! [Automatic Windows support]: #windows
47//! [Built-in (optional) conditions]: Condition#built-in-conditions
48//! [`Style` constructors are `const`]: #uniform-const-builders
49//! [hyperlinking]: hyperlink
50//! [Featureful `no_std`]: #crate-features
51//!
52//! # Usage
53//!
54//! The [`Paint`] trait is implemented for every type. Import it and call
55//! chainable builder methods:
56//!
57//! ```rust
58//! use yansi::Paint;
59//!
60//! println!("Testing, {}, {}, {}!",
61//! "Ready".bold(),
62//! "Set".yellow().italic(),
63//! "STOP".white().on_red().bright().underline().bold());
64//! ```
65//!
66//! `>` Testing,
67//! <b>Ready</b>,
68//! <span style="color: yellow;"><i><b>Set</b></i></span>,
69//! <span style="color: white; background: red;"><u><b>STOP</b></u></span>!
70//!
71//! The methods return a [`Painted`] type which consists of a [`Style`] and a
72//! reference to the receiver. Displaying a [`Painted`] (via `print!()`,
73//! `format!()`, etc) results in emitting ANSI escape codes that effectuate the
74//! style.
75//!
76//! ## Uniform `const` Builders
77//!
78//! All builder methods are uniformly available for [`Style`], [`Color`], and
79//! [`Painted`], which means you can chain calls across library types. All
80//! methods are `const`, allowing creations of `const` or `static` [`Style`]s. A
81//! `Style` can be directly applied to values with [`.paint()`](Paint::paint()),
82//! from [`Paint::paint()`], available for every type:
83//!
84//! ```rust
85//! use yansi::{Paint, Style, Color::*};
86//!
87//! // `const` constructors allow static `Style`s for easy reuse
88//! static ALERT: Style = White.bright().underline().italic().on_red();
89//!
90//! println!("Testing, {}, {}, {}!",
91//! "Ready".bold(),
92//! "Set".yellow().bold(),
93//! "STOP".paint(ALERT));
94//! ```
95//!
96//! `>` Testing,
97//! <b>Ready</b>,
98//! <span style="color: yellow;"><b>Set</b></span>,
99//! <span style="color: white; background: red;"><u><em>STOP</em></u></span>!
100//!
101//! ## Conditional Styling
102//!
103//! ### Globally
104//!
105//! Styling is enabled by default but can be enabled and disabled globally via
106//! [`enable()`] and [`disable()`]. When styling is disabled, no ANSI escape
107//! codes are emitted, and [_masked_] values are omitted entirely.
108//!
109//! Global styling can also be dynamically enabled and disabled using
110//! [`whenever()`] with an arbitrary [`Condition`]: a function that returns
111//! `true` or `false`. This condition is evaluated each time a [`Painted`] item
112//! is displayed. The associated styling is enabled, and mask values emitted,
113//! exactly when and only when the condition returns `true`.
114//!
115//! ### Per-`Style`
116//!
117//! A specific `Style` can itself be conditionally applied by using
118//! [`.whenever()`](Style::whenever()):
119//!
120//! ```rust
121//! # #[cfg(feature = "detect-tty")] {
122//! use yansi::{Paint, Style, Color::*, Condition};
123//!
124//! static WARNING: Style = Black.bold().on_yellow().whenever(Condition::STDERR_IS_TTY);
125//!
126//! eprintln!("{}", "Bees can sting!".paint(WARNING));
127//! # }
128//! ```
129//!
130//! With the above, if `stderr` is a TTY, then:
131//! `>` <span style="background: yellow; color: black;"><b>Bees can sting!</b></span>
132//!
133//! If it is not a TTY, styling is not emitted:
134//! `>` Bees can sting!
135//!
136//! See [`Condition`] for a list of built-in conditions which require enabling
137//! crate features.
138//!
139//! # Quirks
140//!
141//! As a convenience, `yansi` implements several "quirks", applicable via
142//! [`Quirk`] and the respective methods, that modify if and how styling is
143//! presented to the terminal. These quirks do not correspond to any ANSI
144//! styling sequences.
145//!
146//! ## Masking
147//!
148//! Items can be arbitrarily _masked_ with the [`mask()`](Paint::mask()) builder
149//! method. Masked values are not emitted when styling is disabled, globally or
150//! for a given style. This allows selective output based on whether styling is
151//! enabled.
152//!
153//! One use for this feature is to print certain characters only when styling is
154//! enabled. For instance, you might wish to emit the 🎨 emoji when coloring is
155//! enabled but not otherwise. This can be accomplished by masking the emoji:
156//!
157//! ```rust
158//! use yansi::Paint;
159//!
160//! println!("I like colors!{}", " 🎨".mask());
161//! ```
162//!
163//! When styling is enabled, this prints: `>` I like colors! 🎨
164//!
165//! With styling disabled, this prints: `>` I like colors!
166//!
167//! ## Wrapping
168//!
169//! **Note:** _Either the `std` or `alloc` feature is required for wrapping.
170//! `std` is enabled by default. See [crate features](#crate-features)._
171//!
172//! Styling can _wrap_ via [`Quirk::Wrap`] or the equivalent
173//! [`wrap()`](Painted::wrap()) constructor. A wrapping style modifies any
174//! styling resets emitted by the internal value so that they correspond to the
175//! wrapping style. In other words, the "reset" style of the wrapped item is
176//! modified to be the style being `.wrap()`d.
177//!
178//! Wrapping is useful in situations where opaque and arbitrary values must be
179//! styled consistently irrespective of any existing styling. For example, a
180//! generic logger might want to style messages based on log levels
181//! consistently, even when those messages may already include styling. Wrapping
182//! exists to enable such consistent styling:
183//!
184//! ```rust
185//! use yansi::Paint;
186//!
187//! // Imagine that `inner` is opaque and we don't know it's styling.
188//! let inner = format!("{} and {}", "Stop".red(), "Go".green());
189//!
190//! // We can use `wrap` to ensure anything in `inner` not styled is blue.
191//! println!("Hey! {}", inner.blue().wrap());
192//! ```
193//!
194//! Thanks to wrapping, this prints:
195//! `>` Hey! <span style="color: blue">
196//! <span style="color: red">Stop</span> and
197//! <span style="color: green">Go</span>
198//! </span>
199//!
200//! Without wrapping, the reset after `"Stop".red()` would not be overwritten:
201//! `>` Hey! <span style="color: red">Stop</span> and <span style="color: green">Go</span>
202//!
203//! Wrapping incurs a performance cost due to an extra allocation and
204//! replacement if the wrapped item has styling applied to it. Otherwise, it
205//! does not allocate nor incur a meaningful performance cost.
206//!
207//! ## Lingering
208//!
209//! Styling can _linger_ beyond a single value via [`Quirk::Linger`] or the
210//! equivalent [`linger()`](Painted::linger()) constructor. A lingering style
211//! does not reset itself after being applied. In other words, the style lingers
212//! on beyond the value it's applied to, until something else resets the
213//! respective styling.
214//!
215//! The complement to lingering is force resetting via [`Quirk::Resetting`] or
216//! the equivalent [`resetting()`](Painted::resetting()) constructor. Force
217//! resetting, as the name implies, forces a reset suffix to be emitted after
218//! the value, irrespective of any lingering applied. It can be used as a way to
219//! finalize a lingering style.
220//!
221//! Lingering itself is useful in situations where a given style is to be
222//! repeated across multiple values, or when style is intended to persist even
223//! across values that are not styled with `yansi`. It also allows avoiding
224//! unnecessarily repeated ANSI code sequences. The examples below illustrate
225//! some scenarios in which lingering is useful:
226//!
227//! ```rust
228//! use yansi::Paint;
229//!
230//! println!("Hello! {} {} things with {} {}?",
231//! "How".magenta().underline().linger(),
232//! "are".italic().linger(),
233//! "you".on_yellow(), // doesn't linger, so all styling is reset here
234//! "today".blue());
235//! ```
236//!
237//! `>` Hello!
238//! <span style="color: magenta;">
239//! <u>How <i>are things with <span style="background: yellow;">you</span></i></u>
240//! </span>
241//! <span style="color: blue;">today</span>?
242//!
243//! ```rust
244//! use yansi::Paint;
245//!
246//! println!("Hello! {} {} things with {} {}?",
247//! "How".magenta().underline().linger(),
248//! "are".italic(), // doesn't linger, so all styling is reset here
249//! "you".on_yellow().linger(),
250//! "today".blue()); // doesn't linger; styling is reset
251//! ```
252//!
253//! `>` Hello!
254//! <span style="color: magenta;">
255//! <u>How <i>are</i></u>
256//! </span>
257//! things with
258//! <span style="background: yellow;">
259//! you
260//! <span style="color: blue;">today</span></span>?
261//!
262//! ```rust
263//! use yansi::Paint;
264//!
265//! println!("{} B {} {} {} F",
266//! "A".red().linger(),
267//! "C".underline().linger(),
268//! "D", // doesn't linger, but no styling applied, thus no reset
269//! "E".resetting()); // explicitly reset
270//! ```
271//!
272//! `>` <span style="color: red;"> A B <u>C D E</u> </span> F
273//!
274//! ## Brightening
275//!
276//! Most pimrary colors are available in regular and _bright_ variants, e.g.,
277//! [`Color::Red`] and [`Color::BrightRed`]. The [`Quirk::Bright`] and
278//! [`Quirk::OnBright`] quirks, typically applied via
279//! [`.bright()`](Painted::bright()) and [`.on_bright()`](Painted::on_bright()),
280//! provide an alternative, convenient mechanism to select the bright variant of
281//! the selected foreground or background color, respectively. The quirk
282//! provides no additional colors and is equivalent to selecting the bright
283//! variants directly.
284//!
285//! ```rust
286//! use yansi::Paint;
287//!
288//! // These are all equivalent.
289//! print!("{}", "Regular".red());
290//! print!("{}", "Bright".bright_red());
291//! print!("{}", "Bright".bright().red());
292//! print!("{}", "Bright".red().bright());
293//!
294//! # static STYLE: yansi::Style = yansi::Color::Green.bold();
295//! // The `bright` quirk lets use choose the bright variants of _any_ color,
296//! // even when the color or style is unknown at the call site.
297//! print!("{}", "Normal".paint(STYLE));
298//! print!("{}", "Bright".paint(STYLE).bright());
299//! ```
300//!
301//! `>` <span style="color: red;">Regular</span>
302//! <span style="color: hotpink;">Bright</span>
303//! <span style="color: hotpink;">Bright</span>
304//! <span style="color: hotpink;">Bright</span>
305//! <span style="color: green;"><b>Normal</b></span>
306//! <span style="color: greenyellow;"><b>Bright</b></span>
307//!
308//! The `bright()` quirk can be applied before or after a color is selected
309//! while having the same effect.
310//!
311//! # Windows
312//!
313//! Styling is supported and enabled automatically on Windows beginning with
314//! the Windows 10 Anniversary Update, or about [96% of all Windows machines
315//! worldwide](https://gs.statcounter.com/os-version-market-share/windows/desktop/worldwide),
316//! and likely closer to 100% of developer machines (e.g., 99% of visitors to
317//! [rocket.rs](https://rocket.rs) on Windows are on Windows 10+).
318//!
319//! Yansi enables styling support on Windows by querying the Windows API on the
320//! first attempt to color. If support is available, it is enabled. If support
321//! is not available, styling is disabled and no styling sequences are emitted.
322//!
323//! # Crate Features
324//!
325//! | Feature | Default? | Also Enables | Notes |
326//! |--------------|----------|--------------|----------------------------------|
327//! | `std` | **Y** | `alloc` | Use `std` library. |
328//! | `alloc` | **Y** | | Use `alloc`. Enables [wrapping]. |
329//! | `detect-tty` | N | `std` | See [optional conditions]. |
330//! | `detect-env` | N | `std` | See [optional conditions]. |
331//! | `hyperlink` | N | `std` | Enables [hyperlinking] support. |
332//!
333//! With `default-features = false`, this crate is `#[no_std]`.
334//!
335//! Without any features enabled, all functionality except [wrapping] is
336//! available. To recover wrapping _with_ `#[no_std]`, set `default-features =
337//! false` and enable the `alloc` feature, which requires `alloc` support.
338//!
339//! [optional conditions]: Condition#built-in-conditions
340//! [wrapping]: #wrapping
341
342#![doc(html_logo_url = "https://raw.githubusercontent.com/SergioBenitez/yansi/master/.github/yansi-logo.png")]
343#![cfg_attr(not(feature = "std"), no_std)]
344#![cfg_attr(feature = "_nightly", feature(doc_cfg))]
345#![deny(missing_docs)]
346
347// FIXME: Remove once `clear()` and `Quirk::Clear` are removed.
348#![allow(useless_deprecated, deprecated)]
349
350#[cfg(all(not(feature = "std"), feature = "alloc"))]
351extern crate alloc;
352
353#[macro_use]
354mod macros;
355mod windows;
356mod attr_quirk;
357mod style;
358mod color;
359mod paint;
360mod global;
361mod condition;
362mod set;
363
364#[cfg(feature = "hyperlink")]
365#[cfg_attr(feature = "_nightly", doc(cfg(feature = "hyperlink")))]
366pub mod hyperlink;
367
368pub use paint::{Painted, Paint};
369pub use attr_quirk::{Attribute, Quirk};
370pub use style::Style;
371pub use color::Color;
372pub use condition::Condition;
373pub use global::{enable, whenever, disable, is_enabled};