term_transcript/svg/
palette.rs

1//! `Palette` and other color-related types.
2
3use std::{error, fmt, str::FromStr};
4
5use anstyle::RgbColor;
6use serde::{Deserialize, Serialize};
7
8use super::data::serde_color;
9
10/// Palette of [16 standard terminal colors][colors] (8 ordinary colors + 8 intense variations).
11///
12/// [colors]: https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit
13#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
14pub struct Palette {
15    /// Ordinary colors.
16    pub colors: TermColors,
17    /// Intense colors.
18    pub intense_colors: TermColors,
19}
20
21/// Returns the palette specified by [`NamedPalette::Gjm8`].
22impl Default for Palette {
23    fn default() -> Self {
24        Self::gjm8()
25    }
26}
27
28impl Palette {
29    const fn dracula() -> Self {
30        Self {
31            colors: TermColors {
32                black: RgbColor(0x28, 0x29, 0x36),
33                red: RgbColor(0xea, 0x51, 0xb2),
34                green: RgbColor(0xeb, 0xff, 0x87),
35                yellow: RgbColor(0x00, 0xf7, 0x69),
36                blue: RgbColor(0x62, 0xd6, 0xe8),
37                magenta: RgbColor(0xb4, 0x5b, 0xcf),
38                cyan: RgbColor(0xa1, 0xef, 0xe4),
39                white: RgbColor(0xe9, 0xe9, 0xf4),
40            },
41            intense_colors: TermColors {
42                black: RgbColor(0x62, 0x64, 0x83),
43                red: RgbColor(0xb4, 0x5b, 0xcf),
44                green: RgbColor(0x3a, 0x3c, 0x4e),
45                yellow: RgbColor(0x4d, 0x4f, 0x68),
46                blue: RgbColor(0x62, 0xd6, 0xe8),
47                magenta: RgbColor(0xf1, 0xf2, 0xf8),
48                cyan: RgbColor(0x00, 0xf7, 0x69),
49                white: RgbColor(0xf7, 0xf7, 0xfb),
50            },
51        }
52    }
53
54    const fn powershell() -> Self {
55        Self {
56            colors: TermColors {
57                black: RgbColor(0x0c, 0x0c, 0x0c),
58                red: RgbColor(0xc5, 0x0f, 0x1f),
59                green: RgbColor(0x13, 0xa1, 0x0e),
60                yellow: RgbColor(0xc1, 0x9c, 0x00),
61                blue: RgbColor(0x00, 0x37, 0xda),
62                magenta: RgbColor(0x88, 0x17, 0x98),
63                cyan: RgbColor(0x3a, 0x96, 0xdd),
64                white: RgbColor(0xcc, 0xcc, 0xcc),
65            },
66            intense_colors: TermColors {
67                black: RgbColor(0x76, 0x76, 0x76),
68                red: RgbColor(0xe7, 0x48, 0x56),
69                green: RgbColor(0x16, 0xc6, 0x0c),
70                yellow: RgbColor(0xf9, 0xf1, 0xa5),
71                blue: RgbColor(0x3b, 0x78, 0xff),
72                magenta: RgbColor(0xb4, 0x00, 0x9e),
73                cyan: RgbColor(0x61, 0xd6, 0xd6),
74                white: RgbColor(0xf2, 0xf2, 0xf2),
75            },
76        }
77    }
78
79    const fn xterm() -> Self {
80        Self {
81            colors: TermColors {
82                black: RgbColor(0, 0, 0),
83                red: RgbColor(0xcd, 0, 0),
84                green: RgbColor(0, 0xcd, 0),
85                yellow: RgbColor(0xcd, 0xcd, 0),
86                blue: RgbColor(0, 0, 0xee),
87                magenta: RgbColor(0xcd, 0, 0xcd),
88                cyan: RgbColor(0, 0xcd, 0xcd),
89                white: RgbColor(0xe5, 0xe5, 0xe5),
90            },
91            intense_colors: TermColors {
92                black: RgbColor(0x7f, 0x7f, 0x7f),
93                red: RgbColor(0xff, 0, 0),
94                green: RgbColor(0, 0xff, 0),
95                yellow: RgbColor(0xff, 0xff, 0),
96                blue: RgbColor(0x5c, 0x5c, 0xff),
97                magenta: RgbColor(0xff, 0, 0xff),
98                cyan: RgbColor(0, 0xff, 0xff),
99                white: RgbColor(0xff, 0xff, 0xff),
100            },
101        }
102    }
103
104    const fn ubuntu() -> Self {
105        Self {
106            colors: TermColors {
107                black: RgbColor(0x01, 0x01, 0x01),
108                red: RgbColor(0xde, 0x38, 0x2b),
109                green: RgbColor(0x38, 0xb5, 0x4a),
110                yellow: RgbColor(0xff, 0xc7, 0x06),
111                blue: RgbColor(0, 0x6f, 0xb8),
112                magenta: RgbColor(0x76, 0x26, 0x71),
113                cyan: RgbColor(0x2c, 0xb5, 0xe9),
114                white: RgbColor(0xcc, 0xcc, 0xcc),
115            },
116            intense_colors: TermColors {
117                black: RgbColor(0x80, 0x80, 0x80),
118                red: RgbColor(0xff, 0, 0),
119                green: RgbColor(0, 0xff, 0),
120                yellow: RgbColor(0xff, 0xff, 0),
121                blue: RgbColor(0, 0, 0xff),
122                magenta: RgbColor(0xff, 0, 0xff),
123                cyan: RgbColor(0, 0xff, 0xff),
124                white: RgbColor(0xff, 0xff, 0xff),
125            },
126        }
127    }
128
129    const fn gjm8() -> Self {
130        Self {
131            colors: TermColors {
132                black: RgbColor(0x1c, 0x1c, 0x1c),
133                red: RgbColor(0xff, 0x00, 0x5b),
134                green: RgbColor(0xce, 0xe3, 0x18),
135                yellow: RgbColor(0xff, 0xe7, 0x55),
136                blue: RgbColor(0x04, 0x8a, 0xc7),
137                magenta: RgbColor(0x83, 0x3c, 0x9f),
138                cyan: RgbColor(0x0a, 0xc1, 0xcd),
139                white: RgbColor(0xe5, 0xe5, 0xe5),
140            },
141            intense_colors: TermColors {
142                black: RgbColor(0x66, 0x66, 0x66),
143                red: RgbColor(0xff, 0x00, 0xa0),
144                green: RgbColor(0xcc, 0xff, 0x00),
145                yellow: RgbColor(0xff, 0x9f, 0x00),
146                blue: RgbColor(0x48, 0xc6, 0xff),
147                magenta: RgbColor(0xbe, 0x67, 0xe1),
148                cyan: RgbColor(0x63, 0xe7, 0xf0),
149                white: RgbColor(0xf3, 0xf3, 0xf3),
150            },
151        }
152    }
153}
154
155/// Values of [8 base terminal colors][colors].
156///
157/// [colors]: https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit
158#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
159pub struct TermColors {
160    /// Black color.
161    #[serde(with = "serde_color")]
162    pub black: RgbColor,
163    /// Red color.
164    #[serde(with = "serde_color")]
165    pub red: RgbColor,
166    /// Green color.
167    #[serde(with = "serde_color")]
168    pub green: RgbColor,
169    /// Yellow color.
170    #[serde(with = "serde_color")]
171    pub yellow: RgbColor,
172    /// Blue color.
173    #[serde(with = "serde_color")]
174    pub blue: RgbColor,
175    /// Magenta color.
176    #[serde(with = "serde_color")]
177    pub magenta: RgbColor,
178    /// Cyan color.
179    #[serde(with = "serde_color")]
180    pub cyan: RgbColor,
181    /// White color.
182    #[serde(with = "serde_color")]
183    pub white: RgbColor,
184}
185
186/// Named [`Palette`].
187#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
188#[non_exhaustive]
189pub enum NamedPalette {
190    /// Dracula color scheme. This is the [`Default`] value.
191    Dracula,
192    /// PowerShell 6 / Windows 10 console color scheme.
193    PowerShell,
194    /// `xterm` color scheme.
195    Xterm,
196    /// Ubuntu terminal color scheme.
197    Ubuntu,
198    /// [gjm8 color scheme](https://terminal.sexy/).
199    #[default]
200    Gjm8,
201}
202
203impl From<NamedPalette> for Palette {
204    fn from(value: NamedPalette) -> Self {
205        match value {
206            NamedPalette::Dracula => Self::dracula(),
207            NamedPalette::PowerShell => Self::powershell(),
208            NamedPalette::Xterm => Self::xterm(),
209            NamedPalette::Ubuntu => Self::ubuntu(),
210            NamedPalette::Gjm8 => Self::gjm8(),
211        }
212    }
213}
214
215impl FromStr for NamedPalette {
216    type Err = NamedPaletteParseError;
217
218    fn from_str(s: &str) -> Result<Self, Self::Err> {
219        match s {
220            "dracula" => Ok(Self::Dracula),
221            "powershell" => Ok(Self::PowerShell),
222            "xterm" => Ok(Self::Xterm),
223            "ubuntu" => Ok(Self::Ubuntu),
224            "gjm8" => Ok(Self::Gjm8),
225            _ => Err(NamedPaletteParseError(())),
226        }
227    }
228}
229
230/// Errors that can occur when [parsing](FromStr) [`NamedPalette`] from a string.
231#[derive(Debug)]
232pub struct NamedPaletteParseError(());
233
234impl fmt::Display for NamedPaletteParseError {
235    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
236        formatter.write_str(
237            "Invalid palette name; allowed names are `dracula`, `powershell`, `xterm`, \
238             `ubuntu` and `gjm8`",
239        )
240    }
241}
242
243impl error::Error for NamedPaletteParseError {}