fluent_ansi/lib.rs
1#![no_std]
2#![warn(clippy::pedantic)]
3#![warn(missing_docs)]
4//! `fluent-ansi` is a library to handle ANSI escape sequences for the terminal.
5//! It is `no_std`, and relies on the [`Display`](core::fmt::Display) trait to render the sequences.
6//!
7//! ```
8//! use fluent_ansi::{prelude::*, Style, Styled};
9//!
10//! let style: Style = Color::RED.bold();
11//! let styled: Styled<&str> = style.applied_to("Some content");
12//!
13//! println!("{}", styled);
14//!
15//! let content_with_escape_sequences = format!("{}", styled);
16//! assert_eq!(content_with_escape_sequences, "\x1b[1;31mSome content\x1b[0m");
17//! ```
18//!
19//! It has modular and composable values, and with its fluent methods, it provides several forms to
20//! reach the same result. For instance, all the lines below result in the same [`Style`] value:
21//!
22//! ```
23//! use fluent_ansi::{prelude::*, ColorTarget, Style, TargetedColor};
24//!
25//! let style: Style = Style::new().set(Effect::Bold, true).set(ColorTarget::Foreground, Some(Color::RED.to_color()));
26//! let style: Style = Style::new().set_effect(Effect::Bold, true).set_color(ColorTarget::Foreground, Some(Color::RED));
27//! let style: Style = Style::new().add(Effect::Bold).add(TargetedColor::new(Color::RED, ColorTarget::Foreground));
28//! let style: Style = Style::new().effect(Effect::Bold).color(TargetedColor::new(Color::RED, ColorTarget::Foreground));
29//! let style: Style = Style::new().bold().fg(Color::RED);
30//! let style: Style = Effect::Bold.fg(Color::RED);
31//! let style: Style = Color::RED.for_fg().bold();
32//! ```
33//!
34//! All styling types are immutable and implement [`Copy`], except for [`Styled<C>`](Styled),
35//! which is copiable only if its content `C` type is also copiable.
36//!
37//!
38//! # Styling types
39//!
40//! The styling types are categorized according to the following:
41//!
42//! * [Styling element types]:
43//! * [Effect types](#effect-types):
44//! * [`Effect`]
45//! * [`UnderlineEffect`]
46//! * [Color types](#color-types):
47//! * [`TargetedColor`]
48//! * The color types in [`color`]
49//! * [Composed styling types]:
50//! * [`Style`]
51//! * [`Styled<C>`]
52//!
53//!
54//! ## Styling element types
55//!
56//! Each styling element type represents a single styling.
57//! They can be used on their own or -- through their fluent methods -- combined with other styling elements, or applied to some content:
58//!
59//! ```
60//! use fluent_ansi::prelude::*;
61//!
62//! assert_eq!(format!("{}", Effect::Bold), "\x1b[1m");
63//! assert_eq!(format!("{}", Effect::Bold.fg(Color::RED)), "\x1b[1;31m");
64//! assert_eq!(format!("{}", Effect::Bold.applied_to("Some content")), "\x1b[1mSome content\x1b[0m");
65//! ```
66//!
67//! When composed, they result in the [`Style`] [composed styling type].
68//!
69//! When applied to some content, they result in the [`Styled<C>`] [composed styling type].
70//!
71//!
72//! ### Effect types
73//!
74//! An effect is an styling element type that may or may not be present. They correspond to the variants in the [`Effect`] enum.
75//!
76//! A subset of effects correspond to underline effects. They are mutually exclusive, meaning that when
77//! one of them is set, any previously set underline effect is cleared.
78//!
79//! ```
80//! use fluent_ansi::prelude::*;
81//!
82//! let style = Effect::Bold.add(Effect::DottedUnderline);
83//! assert!(style.get_effect(Effect::Bold));
84//! assert!(style.get_effect(Effect::DottedUnderline));
85//!
86//! let style = style.add(Effect::DashedUnderline);
87//! assert!(style.get_effect(Effect::Bold));
88//! assert!(!style.get_effect(Effect::DottedUnderline));
89//! assert!(style.get_effect(Effect::DashedUnderline));
90//! ```
91//!
92//! The [`UnderlineEffect`] enum variants represent the underline effects.
93//!
94//!
95//! ### Color types
96//!
97//! There is a handful of color types, which are defined in the [`color`] module.
98//!
99//! A color is rendered in a [`ColorTarget`], which is [`Foreground`](ColorTarget::Foreground),
100//! [`Background`](ColorTarget::Background) or [`Underline`](ColorTarget::Underline).
101//!
102//! The type [`TargetedColor`] associates a color with a [`ColorTarget`]:
103//!
104//! ```
105//! use fluent_ansi::{prelude::*, TargetedColor};
106//!
107//! let red_foreground: TargetedColor = Color::RED.for_fg();
108//! assert_eq!(format!("{}", red_foreground.applied_to("Some content")), "\x1b[31mSome content\x1b[0m");
109//!
110//! let red_background: TargetedColor = Color::RED.for_bg();
111//! assert_eq!(format!("{}", red_background.applied_to("Some content")), "\x1b[41mSome content\x1b[0m");
112//!
113//! let red_underline: TargetedColor = Color::RED.for_underline();
114//! assert_eq!(format!("{}", red_underline.applied_to("Some content")), "\x1b[58;5;1mSome content\x1b[0m");
115//! ```
116//!
117//! By default, colors are rendered in the foreground:
118//!
119//! ```
120//! use fluent_ansi::prelude::*;
121//!
122//! let rendered_1 = format!("{}", Color::RED.applied_to("Some content"));
123//! let rendered_2 = format!("{}", Color::RED.for_fg().applied_to("Some content"));
124//! assert_eq!(rendered_1, rendered_2);
125//! assert_eq!(rendered_1, "\x1b[31mSome content\x1b[0m");
126//! ```
127//!
128//! You can refrain from mentioning the color target when using only in the foreground. But
129//! if another color target is being used too, be explicit about the foreground target:
130//!
131//! ```
132//! use fluent_ansi::prelude::*;
133//!
134//! // Only the foreground is set
135//! let style = Effect::Bold.add(Color::RED);
136//! let rendered = format!("{}", style.applied_to("Some content"));
137//! assert_eq!(rendered, "\x1b[1;31mSome content\x1b[0m");
138//!
139//! // Both foreground and underline colors are set
140//! let style = Effect::Bold
141//! .add(Color::RED.for_fg())
142//! .add(Color::indexed(42).for_underline());
143//! let rendered = format!("{}", style.applied_to("Some content"));
144//! assert_eq!(rendered, "\x1b[1;31;58;5;42mSome content\x1b[0m");
145//! ```
146//!
147//! ## Composed styling types
148//!
149//! [`Style`] is the result of composing [styling element] values. A [`Style`] can be used on its own or -- through
150//! their methods -- compose with other styling elements, or applied to some content:
151//!
152//! ```
153//! use fluent_ansi::{prelude::*, Style};
154//!
155//! let style: Style = Effect::Bold.fg(Color::RED);
156//! assert_eq!(format!("{style}"), "\x1b[1;31m");
157//!
158//! let style: Style = style.effect(Effect::SolidUnderline);
159//! assert_eq!(format!("{style}"), "\x1b[1;4;31m");
160//!
161//! let styled = style.applied_to("Some content");
162//! assert_eq!(format!("{styled}"), "\x1b[1;4;31mSome content\x1b[0m");
163//! ```
164//!
165//! [`Styled<C>`] includes a [`Style`] and some content to have the styling applied to. The content can
166//! be any type that implements [`Display`](core::fmt::Display). When rendered, the content is preceded by the
167//! escape sequence corresponding to the styling, and is succeeded by the escape sequence that resets the styling.
168//!
169//! A [`Styled<C>`] instance is obtained with the `applied_to()` method available in any [styling element type]
170//! and in [`Style`], or with [`Styled<C>::new()`] to create an instance without any styling.
171//!
172//! ```
173//! use fluent_ansi::{prelude::*, Styled};
174//!
175//! assert_eq!(format!("{}", Effect::Bold.applied_to("Some content")), "\x1b[1mSome content\x1b[0m");
176//! assert_eq!(format!("{}", Color::RED.applied_to("Some content")), "\x1b[31mSome content\x1b[0m");
177//! assert_eq!(format!("{}", Color::RED.bold().applied_to("Some content")), "\x1b[1;31mSome content\x1b[0m");
178//! assert_eq!(format!("{}", Styled::new("Some content").bold().fg(Color::RED)), "\x1b[1;31mSome content\x1b[0m");
179//! ```
180//!
181//! # Styling methods
182//!
183//! Since all types are immutable, all methods return a new value, that is a [composed styling type], which is:
184//!
185//! * [`Styled<C>`] when the method is called from that type,
186//! * [`Style`] when the method is called from any other type.
187//!
188//! There are three group of methods, according to how styling is handled:
189//! * [fluent](#fluent-methods) methods
190//! * methods for handling styling as [elements and sets](#methods-for-elements-and-sets)
191//! * methods for handling styling as [attributes](#methods-for-attributes)
192//!
193//! <div class="warning">
194//!
195//! In the methods docs below, the links take to their implementation in [`Style`], but they are the same for all types.
196//!
197//! </div>
198//!
199//! <div class="warning">
200//!
201//! Although some methods below are documented with varying signatures (e.g. `color(TargetedColor)` and `color(impl Into<Color>)`),
202//! each method name has a single implementation with a generic argument in each type. Check the linked method documentation to see
203//! the real signature.
204//!
205//! </div>
206//!
207//!
208//! ## Fluent methods
209//!
210//! The fluent methods allow to _compose_/_add_/_set_ styling. They are available in all [styling types](#styling-types).
211//!
212//! | Method | To set what |
213//! |--------|-------------|
214//! | [`bold()`](Style::bold),<br/>[`italic()`](Style::italic),<br/>[`solid_underline()`](Style::solid_underline),<br/>etc. | effect |
215//! | [`effect(impl Into<Effect>)`](Style::effect) | effect<br/>(including underline effects) |
216//! | [`underline_effect(UnderlineEffect)`](Style::underline_effect) | underline effect |
217//! | [`fg(impl Into<Color>)`](Style::fg)<br/>[`bg(impl Into<Color>)`](Style::bg)<br/>[`underline_color(impl Into<Color>)`](Style::underline_color) | color |
218//! | [`color(TargetedColor)`](Style::color) | color |
219//! | [`color(impl Into<Color>)`](Style::color) | foreground color |
220//! | [`applied_to(impl Display)`](Style::applied_to) [^applied-to-method] | content | See note \[3] below. |
221//!
222//!
223//! ## Methods for elements and sets
224//!
225//! All [styling types] can be viewed as _styling sets_ where [styling elements] can be added to or removed from.
226//!
227//! The `add` method adds an _element_ to a _set_, and the `remove` method removes an _element_ from a _set_.
228//!
229//!
230//! ### The `add` method
231//!
232//! The `add` method can be used to _compose_/_add_/_set_ some styling, and is available in all [styling types].
233//!
234//! | Method | To add what |
235//! |--------|-------------|
236//! | [`add(Effect)`](Style::add) | effect |
237//! | [`add(UnderlineEffect)`](Style::add) | underline effect |
238//! | [`add(TargetedColor)`](Style::add) | color |
239//! | [`add(impl Into<Color>)`](Style::add) | foreground color |
240//!
241//! ### The `remove` method
242//!
243//! The `remove` method can be used to _clear_/_remove_ some styling, and is available in the [composed styling types].
244//!
245//! | Method | To remove what | Note |
246//! |--------|----------------|------|
247//! | [`remove(Effect)`](Style::remove) | effect |
248//! | [`remove(UnderlineEffect)`](Style::remove) | underline effect | Remove the specific effect, if set |
249//! | [`remove(UnderlineStyle)`](Style::remove) | underline effect | Remove any underline effect that may be set |
250//! | [`remove(ColorTarget)`](Style::remove) | color |
251//!
252//! ### Example
253//!
254//! ```
255//! use fluent_ansi::{prelude::*, ColorTarget, UnderlineStyle, Styled};
256//!
257//! let styled = Styled::new("Some content")
258//! .add(Effect::Bold)
259//! .add(Effect::DottedUnderline)
260//! .add(Color::RED.for_underline());
261//! assert_eq!(format!("{styled}"), "\x1b[1;4:4;58;5;1mSome content\x1b[0m");
262//!
263//! // ...
264//!
265//! let altered_styled = styled
266//! .remove(UnderlineStyle)
267//! .remove(ColorTarget::Underline)
268//! .add(Color::indexed(42).for_bg());
269//! assert_eq!(format!("{altered_styled}"), "\x1b[1;48;5;42mSome content\x1b[0m");
270//! ```
271//!
272//!
273//! ## Methods for attributes
274//!
275//! Styling can be seen as _attributes_, which have _values_. The type of the _value_ varies according to the _attribute_.
276//!
277//! | Attribute | Value type | Meaning |
278//! |-----------|------------|---------|
279//! | [`Effect`] | [`bool`] | Whether the effect is set or not |
280//! | [`UnderlineEffect`] | [`bool`] | Whether the specific underline effect is set or not |
281//! | [`UnderlineStyle`] | [`Option<UnderlineEffect>`] | Which underline effect is in use, if any |
282//! | [`ColorTarget`] | [`Option<Color>`] | Which color is in use for that target, if any |
283//!
284//! The `set` method can be used to _add_/_set_/_clear_/_remove_ some styling, and the `get` method can be used to _query_ any styling.
285//! Both methods are available in all [composed styling types].
286//!
287//! There are also styling-specific variations for the `set` and `get` methods, in addition to the `get_effects()`, that returns an
288//! iterator on the effects that are set.
289//!
290//! | Methods | Styling |
291//! |---------|---------|
292//! | [`set(Effect, bool)`](Style::set) <br/> [`set_effect(Effect, bool)`](Style::set_effect) | effect
293//! | [`set(UnderlineEffect, bool)`](Style::set) <br/> [`set_effect(UnderlineEffect, bool)`](Style::set_effect) | underline effect |
294//! | [`set(UnderlineStyle, Option<UnderlineEffect>)`](Style::set) <br/> [`set_underline_effect(Option<UnderlineEffect>)`](Style::set_underline_effect) | underline effect |
295//! | [`set(ColorTarget, Option<Color>)`](Style::set) <br/> [`set_color(ColorTarget, Option<impl Into<Color>>)`](Style::set_color) [^set-color-none] | color |
296//!
297//! | Methods | Styling |
298//! |---------|---------|
299//! | [`get(Effect) -> bool`](Style::get) <br/> [`get_effect(Effect) -> bool`](Style::get_effect) <br/>[`get_effects() -> GetEffects`](Style::get_effect) | effect
300//! | [`get(UnderlineEffect) -> bool`](Style::get) <br/> [`get_effect(UnderlineEffect) -> bool`](Style::get_effect) | underline effect |
301//! | [`get(UnderlineStyle) -> Option<UnderlineEffect>`](Style::get) <br/> [`get_underline_effect() -> Option<UnderlineEffect>`](Style::get_underline_effect) | underline effect |
302//! | [`get(ColorTarget) -> Option<Color>`](Style::get) <br/> [`get_color(ColorTarget) -> Option<Color>)`](Style::get_color) | color |
303//!
304//! ### Example
305//!
306//! ```
307//! use fluent_ansi::{prelude::*, ColorTarget, UnderlineStyle, Styled};
308//!
309//! let styled = Styled::new("Some content")
310//! .set(Effect::Bold, true)
311//! .set(Effect::DottedUnderline, true)
312//! .set(ColorTarget::Underline, Some(Color::RED.to_color()));
313//! assert_eq!(format!("{styled}"), "\x1b[1;4:4;58;5;1mSome content\x1b[0m");
314//!
315//! // ...
316//!
317//! let altered_styled = styled
318//! .set(UnderlineStyle, None)
319//! .set(ColorTarget::Underline, None)
320//! .set(ColorTarget::Background, Some(Color::indexed(42).to_color()));
321//! assert_eq!(format!("{altered_styled}"), "\x1b[1;48;5;42mSome content\x1b[0m");
322//! ```
323//!
324//!
325//! # The [`Reset`] singleton
326//!
327//! [`Reset`] is a singleton value that represents the "reset" ANSI code. It can be used to manually control
328//! the starting and ending escape sequences instead of using the [`Styled<C>`] type with an enclosed content.
329//!
330//! ```
331//! use fluent_ansi::{prelude::*, Reset};
332//!
333//! let style = Color::RED.bold();
334//! let output = format!("{style}Some content{Reset}");
335//!
336//! assert_eq!(output, "\x1b[1;31mSome content\x1b[0m");
337//! ```
338//!
339//!
340//! [styling types]: #styling-types
341//! [styling element]: #styling-element-types
342//! [styling elements]: #styling-element-types
343//! [styling element type]: #styling-element-types
344//! [styling element types]: #styling-element-types
345//! [composed styling type]: #composed-styling-types
346//! [composed styling types]: #composed-styling-types
347//!
348//! [^applied-to-method]: [`applied_to()`](Style::applied_to) is not available in [`Styled<C>`] values, and always returns a [`Styled<C>`].
349//! [^set-color-none]: To clear a color with [`set_color()`](Style::set_color), the color type must be specified in the `None` value as e.g.
350//! `None::<Color>`. As an alternative, use the [`Color::none()`](color::Color::none) method.
351
352pub use crate::{
353 effect::*, reset::*, style::*, styled::*, styling_attribute::*, styling_element::*,
354 targeted_color::*,
355};
356
357mod colors;
358mod effect;
359mod reset;
360mod style;
361mod styled;
362mod styling_attribute;
363mod styling_element;
364mod targeted_color;
365
366mod impl_macros;
367
368pub mod color {
369 //! Color types and trait.
370 //!
371 //! There are 4 color types:
372 //! - [`BasicColor`]: 3-bit colors with 8 variants.
373 //! - [`SimpleColor`]: Adds bright variants to the [`BasicColor`]s, totalling 16 colors.
374 //! - [`IndexedColor`]: 8-bit colors (256 colors).
375 //! - [`RGBColor`]: RGB colors (24-bit/true color).
376 //!
377 //! The enum [`Color`] unifies all the color types in a single type and have members to access or create colors of all types:
378 //!
379 //! ```
380 //! use fluent_ansi::{prelude::*, color::{BasicColor, IndexedColor, RGBColor, SimpleColor}};
381 //!
382 //! assert_eq!(Color::RED, BasicColor::Red);
383 //! assert_eq!(Color::RED.bright(), SimpleColor::new_bright(BasicColor::Red));
384 //! assert_eq!(Color::indexed(127), IndexedColor(127));
385 //! assert_eq!(Color::rgb(0, 128, 255), RGBColor::new(0, 128, 255));
386 //! ```
387 //!
388 //! All color types are convertible to [`Color`] and can be used where an `impl Into<Color>` value is expected:
389 //!
390 //! ```
391 //! use fluent_ansi::{prelude::*, color::{BasicColor, IndexedColor, RGBColor, SimpleColor}, ColorTarget, Style};
392 //!
393 //! let style = Style::new();
394 //!
395 //! let _ = style.fg(BasicColor::Red);
396 //! let _ = style.fg(SimpleColor::new_bright(BasicColor::Red));
397 //! let _ = style.fg(IndexedColor::new(128));
398 //! let _ = style.fg(RGBColor::new(0, 128, 255));
399 //!
400 //! let _ = style.bg(BasicColor::Red);
401 //! let _ = style.bg(SimpleColor::new_bright(BasicColor::Red));
402 //! let _ = style.bg(IndexedColor::new(128));
403 //! let _ = style.bg(RGBColor::new(0, 128, 255));
404 //!
405 //! let _ = style.set_color(ColorTarget::Foreground, Some(BasicColor::Red));
406 //! let _ = style.set_color(ColorTarget::Background, Some(SimpleColor::new_bright(BasicColor::Red)));
407 //! let _ = style.set_color(ColorTarget::Foreground, Some(IndexedColor::new(128)));
408 //! let _ = style.set_color(ColorTarget::Background, Some(RGBColor::new(0, 128, 255)));
409 //! ```
410
411 pub use crate::colors::*;
412}
413
414pub mod prelude {
415 //! Re-exports the minimal set of items to style some content.
416 //!
417 //! This module can be imported to have access to the minimal items to build a [`Styled<C>`](crate::Styled) value from
418 //! effects and colors.
419 //!
420 //! ```
421 //! use fluent_ansi::prelude::*;
422 //!
423 //! let styled = Color::RED.for_bg().bold().applied_to("Some content");
424 //! ```
425
426 pub use crate::Effect;
427 pub use crate::UnderlineEffect;
428 pub use crate::color::Color;
429}