1use crate::style::{ClassStyle, StyleBuilder};
2use std::{
3 borrow::Borrow,
4 convert::TryFrom,
5 fmt::{self, Display, Formatter},
6 iter,
7};
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub enum ColorName {
12 Black,
13 Red,
14 Green,
15 Yellow,
16 Blue,
17 Magenta,
18 Cyan,
19 White,
20}
21impl ColorName {
22 fn from_code(code: usize) -> Option<Self> {
23 use ColorName::*;
24 [Black, Red, Green, Yellow, Blue, Magenta, Cyan, White]
25 .get(code % 10)
26 .copied()
27 }
28
29 pub fn rgb(self, bright: bool) -> u32 {
31 use ColorName::*;
32
33 macro_rules! rgb {
34 ($r:literal, $g:literal, $b:literal) => {
35 (($r << 16) + ($g << 8) + $b) as u32
36 };
37 }
38
39 if bright {
40 match self {
41 Black => rgb!(1, 1, 1),
42 Red => rgb!(222, 56, 43),
43 Green => rgb!(57, 181, 74),
44 Yellow => rgb!(255, 199, 6),
45 Blue => rgb!(0, 111, 184),
46 Magenta => rgb!(118, 38, 113),
47 Cyan => rgb!(44, 181, 233),
48 White => rgb!(204, 204, 204),
49 }
50 } else {
51 match self {
52 Black => rgb!(128, 128, 128),
53 Red => rgb!(255, 0, 0),
54 Green => rgb!(0, 255, 0),
55 Yellow => rgb!(255, 255, 0),
56 Blue => rgb!(0, 0, 255),
57 Magenta => rgb!(255, 0, 255),
58 Cyan => rgb!(0, 255, 255),
59 White => rgb!(255, 255, 255),
60 }
61 }
62 }
63}
64impl Display for ColorName {
65 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
66 use ColorName::*;
67 let name = match self {
68 Black => "black",
69 Red => "red",
70 Green => "green",
71 Yellow => "yellow",
72 Blue => "blue",
73 Magenta => "magenta",
74 Cyan => "cyan",
75 White => "white",
76 };
77 f.write_str(name)
78 }
79}
80
81#[derive(Clone, Debug, Eq, PartialEq)]
83#[non_exhaustive]
84pub enum Sgr {
85 Reset,
86 Bold,
87 BoldOff,
88 Italic,
89 ItalicOff,
90 Underline,
91 UnderlineOff,
92 ColorFgRgb(u32),
93 ColorFgName(ColorName),
94 ColorFgNameBright(ColorName),
95 ResetColorFg,
96 ColorBgRgb(u32),
97 ColorBgName(ColorName),
98 ColorBgNameBright(ColorName),
99 ResetColorBg,
100}
101impl Sgr {
102 fn from_color_code(code: usize, background: bool, bright: bool) -> Option<Self> {
103 use Sgr::*;
104 let color = ColorName::from_code(code)?;
105 let sgr = match (background, bright) {
106 (false, false) => ColorFgName(color),
107 (false, true) => ColorFgNameBright(color),
108 (true, false) => ColorBgName(color),
109 (true, true) => ColorBgNameBright(color),
110 };
111 Some(sgr)
112 }
113
114 fn from_rgb(r: usize, g: usize, b: usize, background: bool) -> Option<Self> {
115 let rgb = u32::try_from((r << 16) + (g << 8) + b).ok()?;
116 let sgr = if background {
117 Self::ColorBgRgb(rgb)
118 } else {
119 Self::ColorFgRgb(rgb)
120 };
121 Some(sgr)
122 }
123
124 fn color_rgb(mut params: impl Iterator<Item = usize>, background: bool) -> Option<Self> {
125 match params.next()? {
126 2 => {
127 let (r, g, b) = (params.next()?, params.next()?, params.next()?);
128 Self::from_rgb(r, g, b, background)
129 }
130 5 => {
131 let n = params.next()?;
132 match n {
133 0..=7 => Self::from_color_code(n, background, false),
134 8..=15 => Self::from_color_code(n - 8, background, true),
135 16..=231 => {
136 const ROWS: usize = 6;
141 const COLUMNS: usize = 36;
142 const STEP_SIZE: usize = 0xFF / 6;
143
144 let n = n - 16;
145 let r = (n / COLUMNS) * STEP_SIZE;
147 let g = ((n % COLUMNS) / ROWS) * STEP_SIZE;
149 let b = (n % ROWS) * STEP_SIZE;
151 Self::from_rgb(r, g, b, background)
152 }
153 232..=255 => {
154 const STEP_SIZE: usize = 0xFF / 24;
155 let n = n - 232;
156 Self::from_rgb(n * STEP_SIZE, n * STEP_SIZE, n * STEP_SIZE, background)
157 }
158 _ => None,
159 }
160 }
161 _ => None,
162 }
163 }
164
165 fn from_params(mut params: impl Iterator<Item = usize>) -> Option<Self> {
168 use Sgr::*;
169 let code = params.next()?;
170 Some(match code {
171 0 => Reset,
172 1 => Bold,
173 3 => Italic,
174 4 => Underline,
175 22 => BoldOff,
176 23 => ItalicOff,
177 24 => UnderlineOff,
178 30..=37 => ColorFgName(ColorName::from_code(code)?),
179 38 => Self::color_rgb(params, false)?,
180 39 => ResetColorFg,
181 40..=47 => ColorBgName(ColorName::from_code(code)?),
182 48 => Self::color_rgb(params, true)?,
183 49 => ResetColorBg,
184 90..=97 => ColorFgNameBright(ColorName::from_code(code)?),
185 100..=107 => ColorBgNameBright(ColorName::from_code(code)?),
186 _ => return None,
187 })
188 }
189}
190
191pub(crate) fn parse_sgrs(mut params: impl Iterator<Item = usize>) -> Vec<Sgr> {
194 iter::from_fn(|| Sgr::from_params(&mut params)).collect()
195}
196
197#[derive(Clone, Debug, Eq, PartialEq)]
199pub enum ColorEffect {
200 None,
201 Name(ColorName),
202 NameBright(ColorName),
203 Rgb(u32),
204}
205impl ColorEffect {
206 pub fn rgb(&self) -> Option<u32> {
208 match self {
209 Self::None => None,
210 Self::Name(named) => Some(named.rgb(false)),
211 Self::NameBright(named) => Some(named.rgb(true)),
212 Self::Rgb(rgb) => Some(*rgb),
213 }
214 }
215}
216impl Default for ColorEffect {
217 fn default() -> Self {
218 Self::None
219 }
220}
221
222impl From<&Sgr> for ColorEffect {
223 fn from(sgr: &Sgr) -> Self {
224 use Sgr::*;
225 match sgr {
226 ColorFgRgb(rgb) | ColorBgRgb(rgb) => Self::Rgb(*rgb),
227 ColorFgName(name) | ColorBgName(name) => Self::Name(*name),
228 ColorFgNameBright(name) | ColorBgNameBright(name) => Self::NameBright(*name),
229 _ => Self::None,
230 }
231 }
232}
233
234#[derive(Clone, Debug, Default, Eq, PartialEq)]
236pub struct SgrEffect {
237 pub bold: bool,
238 pub italic: bool,
239 pub underline: bool,
240 pub fg: ColorEffect,
242 pub bg: ColorEffect,
244}
245impl SgrEffect {
246 fn reset(&mut self) {
247 *self = Self::default();
248 }
249
250 pub fn apply_sgr(&mut self, sgr: impl Borrow<Sgr>) {
252 use Sgr::*;
253 let sgr = sgr.borrow();
254 match sgr {
255 Reset => self.reset(),
256 Bold => self.bold = true,
257 BoldOff => self.bold = false,
258 Italic => self.italic = true,
259 ItalicOff => self.italic = false,
260 Underline => self.underline = true,
261 UnderlineOff => self.underline = false,
262 ColorFgRgb(_) | ColorFgName(_) | ColorFgNameBright(_) | ResetColorFg => {
263 self.fg = ColorEffect::from(sgr);
264 }
265 ColorBgRgb(_) | ColorBgName(_) | ColorBgNameBright(_) | ResetColorBg => {
266 self.bg = ColorEffect::from(sgr);
267 }
268 }
269 }
270
271 pub fn apply_sgrs<T: Borrow<Sgr>>(&mut self, sgrs: impl IntoIterator<Item = T>) {
273 for sgr in sgrs {
274 self.apply_sgr(sgr.borrow());
275 }
276 }
277
278 pub fn to_class_style<B: StyleBuilder>(&self) -> ClassStyle {
279 let mut builder = B::default();
280 if self.bold {
281 builder.bold();
282 }
283 if self.italic {
284 builder.italic();
285 }
286 if self.underline {
287 builder.underline();
288 }
289 builder.fg_color(&self.fg);
290 builder.bg_color(&self.bg);
291
292 builder.finish()
293 }
294}