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;