parse_style/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2//! `parse-style` is a [Rust](https://www.rust-lang.org) library for parsing &
3//! displaying strings describing styles for terminal text using a syntax
4//! compatible with the Python library
5//! [`rich`](https://github.com/Textualize/rich).
6//!
7//! ```
8//! use parse_style::{Color256, Style};
9//!
10//! assert_eq!(
11//!     "bold red on blue".parse::<Style>().unwrap(),
12//!     Style::new()
13//!         .foreground(Some(Color256::RED.into()))
14//!         .background(Some(Color256::BLUE.into()))
15//!         .bold()
16//! );
17//!
18//! let style = Style::from(Color256::BRIGHT_GREEN).underline();
19//! assert_eq!(style.to_string(), "underline bright_green");
20//! ```
21//!
22//! Note that this library does not provide functionality for rendering styles
23//! as ANSI escape sequences; there are plenty of crates that do that already,
24//! and `parse-style` provides conversions to some of those crates' types so
25//! you can use them for your actual styling.
26//!
27//! Style String Syntax
28//! ===================
29//!
30//! `parse-style` follows `rich`'s [style string syntax specification][syntax],
31//! specifically:
32//!
33//! - A style string may be the empty string or `"none"` (case insensitive),
34//!   denoting an empty style, or it may be a space-separated sequence of one or
35//!   more of the following tokens in any order:
36//!
37//!     - A color word (see below), denoting a foreground color
38//!
39//!     - The word "`on`" followed by a color word, denoting a background color
40//!
41//!     - One of the following attribute names (case insensitive), indicating that
42//!       the given text attribute should be enabled:
43//!         - "`bold`" or "`b`" — bold text
44//!         - "`dim`" or "`d`" — dim/faint text
45//!         - "`italic`" or "`i`" — italic text
46//!         - "`underline`" or "`u`" — underlined text
47//!         - "`blink`" — blinking text
48//!         - "`blink2`" — fast/rapidly blinking text
49//!         - "`reverse`" or "`r`" — reverse-video text
50//!         - "`conceal`" or "`c`" — concealed/hidden text
51//!         - "`strike`" or "`s`" — struck-through text
52//!         - "`underline2`" or "`uu`" — double-underlined text
53//!         - "`frame`" — framed text
54//!         - "`encircle`" — encircled text
55//!         - "`overline`" — overlined text
56//!
57//!     - The word "`not`" followed by one of the above attribute names, indicating
58//!       that the given text attribute should be disabled
59//!
60//! - Colors may be specified as any of the following:
61//!
62//!     - "`default`" (case insensitive), denoting the default terminal foreground
63//!       or background color
64//!
65//!     - a color name (case insensitive) from [this table][colors]
66//!
67//!     - a word of the form `color({index})` (case insensitive) where
68//!       `{index}` is a decimal integer from 0 through 255, denoting the 8-bit
69//!       color with the given index
70//!
71//!     - a word of the form `#xxxxxx`, where the `x`'s are hexadecimal digits,
72//!       denoting an RGB color
73//!
74//!     - a word of the form `rgb({red},{green},{blue})` (case insensitive) where
75//!       `{red}`, `{green}`, and `{blue}` are decimal integers from 0 though 255,
76//!       denoting an RGB color
77//!
78//! - If a style string contains two or more foreground colors, the last one is
79//!   used, and likewise for background colors.
80//!
81//! - If a style string contains both an attribute and `not` the same attribute,
82//!   the last occurrence wins.
83//!
84//! [syntax]: https://rich.readthedocs.io/en/stable/style.html
85//! [colors]: https://rich.readthedocs.io/en/stable/appendix/colors.html
86//!
87//! Differences from `rich` Style Syntax
88//! ------------------------------------
89//!
90//! - Hyperlink syntax (`"link https://www.example.com"`) is not supported.
91//!
92//! - Minor technical difference: `parse-style` uses Rust's definition of
93//!   whitespace for splitting strings into tokens, while `rich` uses Python's
94//!   space definition, which includes a few extra control characters.
95//!
96//! Features
97//! ========
98//!
99//! The `parse-style` crate has the following optional features:
100//!
101//! - `anstyle` — Enables conversions between `parse-style` types and types
102//!   from the [`anstyle`](https://crates.io/crates/anstyle) crate
103//!
104//! - `crossterm` — Enables conversions between `parse-style` types and types
105//!   from the [`crossterm`](https://crates.io/crates/crossterm) crate
106//!
107//! - `ratatui` — Enables conversions between `parse-style` types and types
108//!   from the [`ratatui`](https://crates.io/crates/ratatui) crate
109//!
110//! - `serde` — Enables [`serde`](https://serde.rs) implementations for
111//!   (de)serializing `Style` values as style strings and colors as color
112//!   strings.  When combined with one or more of the above features, also
113//!   enables `#[serde(with)]`-compatible modules for (de)serializing foreign
114//!   types in the same way.
115//!
116//! Important: Lossy Conversions
117//! ============================
118//!
119//! Different terminal text-styling crates support different styling features,
120//! making perfect interoperability impossible.  As a result, some conversions
121//! between `parse-style` types and foreign types must discard some information
122//! due to the target type being unable to represent it.  See the "Data Loss"
123//! sections in the documentation of the `From` impls for specific information.
124
125mod attributes;
126mod color;
127mod color256;
128mod rgbcolor;
129mod style;
130mod util;
131pub use crate::attributes::{
132    Attribute, AttributeIter, AttributeSet, AttributeSetIter, ParseAttributeError,
133};
134pub use crate::color::Color;
135pub use crate::color256::Color256;
136pub use crate::rgbcolor::RgbColor;
137pub use crate::style::{ParseStyleError, Style};
138use thiserror::Error;
139
140#[cfg(feature = "serde")]
141#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
142pub mod serde;
143
144/// Error returned when parsing a color string fails
145#[derive(Clone, Debug, Eq, Error, PartialEq)]
146#[error("invalid color string: {0:?}")]
147pub struct ParseColorError(
148    /// The invalid color string
149    pub String,
150);
151
152/// Error returned when conversion between a `parse_style` type and a foreign
153/// type fails
154#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
155#[error("failed to convert between parse_style type and foreign type")]
156pub struct ConversionError;