1use std::{error, fmt, str::FromStr};
4
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6
7use crate::utils::RgbColor;
8
9#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
13pub struct Palette {
14 pub colors: TermColors,
16 pub intense_colors: TermColors,
18}
19
20impl Default for Palette {
22 fn default() -> Self {
23 Self::gjm8()
24 }
25}
26
27impl Palette {
28 const fn dracula() -> Self {
29 Self {
30 colors: TermColors {
31 black: RgbColor(0x28, 0x29, 0x36),
32 red: RgbColor(0xea, 0x51, 0xb2),
33 green: RgbColor(0xeb, 0xff, 0x87),
34 yellow: RgbColor(0x00, 0xf7, 0x69),
35 blue: RgbColor(0x62, 0xd6, 0xe8),
36 magenta: RgbColor(0xb4, 0x5b, 0xcf),
37 cyan: RgbColor(0xa1, 0xef, 0xe4),
38 white: RgbColor(0xe9, 0xe9, 0xf4),
39 },
40 intense_colors: TermColors {
41 black: RgbColor(0x62, 0x64, 0x83),
42 red: RgbColor(0xb4, 0x5b, 0xcf),
43 green: RgbColor(0x3a, 0x3c, 0x4e),
44 yellow: RgbColor(0x4d, 0x4f, 0x68),
45 blue: RgbColor(0x62, 0xd6, 0xe8),
46 magenta: RgbColor(0xf1, 0xf2, 0xf8),
47 cyan: RgbColor(0x00, 0xf7, 0x69),
48 white: RgbColor(0xf7, 0xf7, 0xfb),
49 },
50 }
51 }
52
53 const fn powershell() -> Self {
54 Self {
55 colors: TermColors {
56 black: RgbColor(0x0c, 0x0c, 0x0c),
57 red: RgbColor(0xc5, 0x0f, 0x1f),
58 green: RgbColor(0x13, 0xa1, 0x0e),
59 yellow: RgbColor(0xc1, 0x9c, 0x00),
60 blue: RgbColor(0x00, 0x37, 0xda),
61 magenta: RgbColor(0x88, 0x17, 0x98),
62 cyan: RgbColor(0x3a, 0x96, 0xdd),
63 white: RgbColor(0xcc, 0xcc, 0xcc),
64 },
65 intense_colors: TermColors {
66 black: RgbColor(0x76, 0x76, 0x76),
67 red: RgbColor(0xe7, 0x48, 0x56),
68 green: RgbColor(0x16, 0xc6, 0x0c),
69 yellow: RgbColor(0xf9, 0xf1, 0xa5),
70 blue: RgbColor(0x3b, 0x78, 0xff),
71 magenta: RgbColor(0xb4, 0x00, 0x9e),
72 cyan: RgbColor(0x61, 0xd6, 0xd6),
73 white: RgbColor(0xf2, 0xf2, 0xf2),
74 },
75 }
76 }
77
78 const fn xterm() -> Self {
79 Self {
80 colors: TermColors {
81 black: RgbColor(0, 0, 0),
82 red: RgbColor(0xcd, 0, 0),
83 green: RgbColor(0, 0xcd, 0),
84 yellow: RgbColor(0xcd, 0xcd, 0),
85 blue: RgbColor(0, 0, 0xee),
86 magenta: RgbColor(0xcd, 0, 0xcd),
87 cyan: RgbColor(0, 0xcd, 0xcd),
88 white: RgbColor(0xe5, 0xe5, 0xe5),
89 },
90 intense_colors: TermColors {
91 black: RgbColor(0x7f, 0x7f, 0x7f),
92 red: RgbColor(0xff, 0, 0),
93 green: RgbColor(0, 0xff, 0),
94 yellow: RgbColor(0xff, 0xff, 0),
95 blue: RgbColor(0x5c, 0x5c, 0xff),
96 magenta: RgbColor(0xff, 0, 0xff),
97 cyan: RgbColor(0, 0xff, 0xff),
98 white: RgbColor(0xff, 0xff, 0xff),
99 },
100 }
101 }
102
103 const fn ubuntu() -> Self {
104 Self {
105 colors: TermColors {
106 black: RgbColor(0x01, 0x01, 0x01),
107 red: RgbColor(0xde, 0x38, 0x2b),
108 green: RgbColor(0x38, 0xb5, 0x4a),
109 yellow: RgbColor(0xff, 0xc7, 0x06),
110 blue: RgbColor(0, 0x6f, 0xb8),
111 magenta: RgbColor(0x76, 0x26, 0x71),
112 cyan: RgbColor(0x2c, 0xb5, 0xe9),
113 white: RgbColor(0xcc, 0xcc, 0xcc),
114 },
115 intense_colors: TermColors {
116 black: RgbColor(0x80, 0x80, 0x80),
117 red: RgbColor(0xff, 0, 0),
118 green: RgbColor(0, 0xff, 0),
119 yellow: RgbColor(0xff, 0xff, 0),
120 blue: RgbColor(0, 0, 0xff),
121 magenta: RgbColor(0xff, 0, 0xff),
122 cyan: RgbColor(0, 0xff, 0xff),
123 white: RgbColor(0xff, 0xff, 0xff),
124 },
125 }
126 }
127
128 const fn gjm8() -> Self {
129 Self {
130 colors: TermColors {
131 black: RgbColor(0x1c, 0x1c, 0x1c),
132 red: RgbColor(0xff, 0x00, 0x5b),
133 green: RgbColor(0xce, 0xe3, 0x18),
134 yellow: RgbColor(0xff, 0xe7, 0x55),
135 blue: RgbColor(0x04, 0x8a, 0xc7),
136 magenta: RgbColor(0x83, 0x3c, 0x9f),
137 cyan: RgbColor(0x0a, 0xc1, 0xcd),
138 white: RgbColor(0xe5, 0xe5, 0xe5),
139 },
140 intense_colors: TermColors {
141 black: RgbColor(0x66, 0x66, 0x66),
142 red: RgbColor(0xff, 0x00, 0xa0),
143 green: RgbColor(0xcc, 0xff, 0x00),
144 yellow: RgbColor(0xff, 0x9f, 0x00),
145 blue: RgbColor(0x48, 0xc6, 0xff),
146 magenta: RgbColor(0xbe, 0x67, 0xe1),
147 cyan: RgbColor(0x63, 0xe7, 0xf0),
148 white: RgbColor(0xf3, 0xf3, 0xf3),
149 },
150 }
151 }
152}
153
154#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
158pub struct TermColors {
159 pub black: RgbColor,
161 pub red: RgbColor,
163 pub green: RgbColor,
165 pub yellow: RgbColor,
167 pub blue: RgbColor,
169 pub magenta: RgbColor,
171 pub cyan: RgbColor,
173 pub white: RgbColor,
175}
176
177impl Serialize for RgbColor {
178 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
179 serializer.serialize_str(&format!("{self:x}"))
180 }
181}
182
183impl<'de> Deserialize<'de> for RgbColor {
184 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
185 use serde::de;
186
187 #[derive(Debug)]
188 struct ColorVisitor;
189
190 impl de::Visitor<'_> for ColorVisitor {
191 type Value = RgbColor;
192
193 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
194 formatter.write_str("hex color, such as #fed or #a757ff")
195 }
196
197 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
198 value.parse().map_err(E::custom)
199 }
200 }
201
202 deserializer.deserialize_str(ColorVisitor)
203 }
204}
205
206#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
208#[non_exhaustive]
209pub enum NamedPalette {
210 Dracula,
212 PowerShell,
214 Xterm,
216 Ubuntu,
218 Gjm8,
220}
221
222impl Default for NamedPalette {
223 fn default() -> Self {
224 Self::Gjm8
225 }
226}
227
228impl From<NamedPalette> for Palette {
229 fn from(value: NamedPalette) -> Self {
230 match value {
231 NamedPalette::Dracula => Self::dracula(),
232 NamedPalette::PowerShell => Self::powershell(),
233 NamedPalette::Xterm => Self::xterm(),
234 NamedPalette::Ubuntu => Self::ubuntu(),
235 NamedPalette::Gjm8 => Self::gjm8(),
236 }
237 }
238}
239
240impl FromStr for NamedPalette {
241 type Err = NamedPaletteParseError;
242
243 fn from_str(s: &str) -> Result<Self, Self::Err> {
244 match s {
245 "dracula" => Ok(Self::Dracula),
246 "powershell" => Ok(Self::PowerShell),
247 "xterm" => Ok(Self::Xterm),
248 "ubuntu" => Ok(Self::Ubuntu),
249 "gjm8" => Ok(Self::Gjm8),
250 _ => Err(NamedPaletteParseError(())),
251 }
252 }
253}
254
255#[derive(Debug)]
257pub struct NamedPaletteParseError(());
258
259impl fmt::Display for NamedPaletteParseError {
260 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
261 formatter.write_str(
262 "Invalid palette name; allowed names are `dracula`, `powershell`, `xterm`, \
263 `ubuntu` and `gjm8`",
264 )
265 }
266}
267
268impl error::Error for NamedPaletteParseError {}