1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
//! 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
//!
//! 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()`](Paint::paint()) method, also from the
//! [`Paint`] trait and thus available for every type:
//!
//! ```rust
//! 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,
//! <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.
//!
//! 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()`](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
//!
//! `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 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:
//!
//! ```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 `red()` reset after "Stop" is not overwritten:
//! `>` Hey! <span style="color: red">Stop</span> and <span style="color: green">Go</span>
//!
//! 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()`](Painted::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()`](Painted::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:
//!
//! ```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 cleared 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 cleared here
//!     "you".on_yellow().linger(),
//!     "today".blue()); // doesn't linger; styling is cleared
//! ```
//!
//! `>` 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 clear
//!     "E".clear());  // explicitly clear
//! ```
//!
//! `>` <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 [95% 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 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.
//!
//! [optional conditions]: Condition#built-in-conditions
//! [wrapping]: #wrapping

#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "_nightly", feature(doc_cfg))]
#![deny(missing_docs)]

#[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};