term_model/term/
color.rs

1use std::fmt;
2use std::ops::{Index, IndexMut, Mul};
3use std::str::FromStr;
4
5use log::{error, trace};
6use serde::de::Visitor;
7use serde::{Deserialize, Deserializer, Serialize};
8
9use crate::ansi;
10use crate::config::Colors;
11
12pub const COUNT: usize = 269;
13
14pub const RED: Rgb = Rgb { r: 0xff, g: 0x0, b: 0x0 };
15pub const YELLOW: Rgb = Rgb { r: 0xff, g: 0xff, b: 0x0 };
16
17#[derive(Debug, Eq, PartialEq, Copy, Clone, Default, Serialize)]
18pub struct Rgb {
19    pub r: u8,
20    pub g: u8,
21    pub b: u8,
22}
23
24// a multiply function for Rgb, as the default dim is just *2/3
25impl Mul<f32> for Rgb {
26    type Output = Rgb;
27
28    fn mul(self, rhs: f32) -> Rgb {
29        let result = Rgb {
30            r: (f32::from(self.r) * rhs).max(0.0).min(255.0) as u8,
31            g: (f32::from(self.g) * rhs).max(0.0).min(255.0) as u8,
32            b: (f32::from(self.b) * rhs).max(0.0).min(255.0) as u8,
33        };
34
35        trace!("Scaling RGB by {} from {:?} to {:?}", rhs, self, result);
36
37        result
38    }
39}
40
41/// Deserialize an Rgb from a hex string
42///
43/// This is *not* the deserialize impl for Rgb since we want a symmetric
44/// serialize/deserialize impl for ref tests.
45impl<'de> Deserialize<'de> for Rgb {
46    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
47    where
48        D: Deserializer<'de>,
49    {
50        struct RgbVisitor;
51
52        // Used for deserializing reftests
53        #[derive(Deserialize)]
54        struct RgbDerivedDeser {
55            r: u8,
56            g: u8,
57            b: u8,
58        }
59
60        impl<'a> Visitor<'a> for RgbVisitor {
61            type Value = Rgb;
62
63            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64                f.write_str("hex color like 0xff00ff")
65            }
66
67            fn visit_str<E>(self, value: &str) -> ::std::result::Result<Rgb, E>
68            where
69                E: ::serde::de::Error,
70            {
71                Rgb::from_str(&value[..])
72                    .map_err(|_| E::custom("failed to parse rgb; expected hex color like 0xff00ff"))
73            }
74        }
75
76        // Return an error if the syntax is incorrect
77        let value = serde_json::Value::deserialize(deserializer)?;
78
79        // Attempt to deserialize from struct form
80        if let Ok(RgbDerivedDeser { r, g, b }) = RgbDerivedDeser::deserialize(value.clone()) {
81            return Ok(Rgb { r, g, b });
82        }
83
84        // Deserialize from hex notation (either 0xff00ff or #ff00ff)
85        match value.deserialize_str(RgbVisitor) {
86            Ok(rgb) => Ok(rgb),
87            Err(err) => {
88                error!("Problem with config: {}; using color #000000", err);
89                Ok(Rgb::default())
90            }
91        }
92    }
93}
94
95impl FromStr for Rgb {
96    type Err = ();
97
98    fn from_str(s: &str) -> ::std::result::Result<Rgb, ()> {
99        let mut chars = s.chars();
100        let mut rgb = Rgb::default();
101
102        macro_rules! component {
103            ($($c:ident),*) => {
104                $(
105                    match chars.next().and_then(|c| c.to_digit(16)) {
106                        Some(val) => rgb.$c = (val as u8) << 4,
107                        None => return Err(())
108                    }
109
110                    match chars.next().and_then(|c| c.to_digit(16)) {
111                        Some(val) => rgb.$c |= val as u8,
112                        None => return Err(())
113                    }
114                )*
115            }
116        }
117
118        match chars.next() {
119            Some('0') => {
120                if chars.next() != Some('x') {
121                    return Err(());
122                }
123            }
124            Some('#') => (),
125            _ => return Err(()),
126        }
127
128        component!(r, g, b);
129
130        Ok(rgb)
131    }
132}
133
134/// List of indexed colors
135///
136/// The first 16 entries are the standard ansi named colors. Items 16..232 are
137/// the color cube.  Items 233..256 are the grayscale ramp. Item 256 is
138/// the configured foreground color, item 257 is the configured background
139/// color, item 258 is the cursor color. Following that are 8 positions for dim colors.
140/// Item 267 is the bright foreground color, 268 the dim foreground.
141#[derive(Copy, Clone)]
142pub struct List([Rgb; COUNT]);
143
144impl<'a> From<&'a Colors> for List {
145    fn from(colors: &Colors) -> List {
146        // Type inference fails without this annotation
147        let mut list = List([Rgb::default(); COUNT]);
148
149        list.fill_named(colors);
150        list.fill_cube(colors);
151        list.fill_gray_ramp(colors);
152
153        list
154    }
155}
156
157impl List {
158    pub fn fill_named(&mut self, colors: &Colors) {
159        // Normals
160        self[ansi::NamedColor::Black] = colors.normal().black;
161        self[ansi::NamedColor::Red] = colors.normal().red;
162        self[ansi::NamedColor::Green] = colors.normal().green;
163        self[ansi::NamedColor::Yellow] = colors.normal().yellow;
164        self[ansi::NamedColor::Blue] = colors.normal().blue;
165        self[ansi::NamedColor::Magenta] = colors.normal().magenta;
166        self[ansi::NamedColor::Cyan] = colors.normal().cyan;
167        self[ansi::NamedColor::White] = colors.normal().white;
168
169        // Brights
170        self[ansi::NamedColor::BrightBlack] = colors.bright().black;
171        self[ansi::NamedColor::BrightRed] = colors.bright().red;
172        self[ansi::NamedColor::BrightGreen] = colors.bright().green;
173        self[ansi::NamedColor::BrightYellow] = colors.bright().yellow;
174        self[ansi::NamedColor::BrightBlue] = colors.bright().blue;
175        self[ansi::NamedColor::BrightMagenta] = colors.bright().magenta;
176        self[ansi::NamedColor::BrightCyan] = colors.bright().cyan;
177        self[ansi::NamedColor::BrightWhite] = colors.bright().white;
178        self[ansi::NamedColor::BrightForeground] =
179            colors.primary.bright_foreground.unwrap_or(colors.primary.foreground);
180
181        // Foreground and background
182        self[ansi::NamedColor::Foreground] = colors.primary.foreground;
183        self[ansi::NamedColor::Background] = colors.primary.background;
184
185        // Background for custom cursor colors
186        self[ansi::NamedColor::Cursor] = colors.cursor.cursor.unwrap_or_else(Rgb::default);
187
188        // Dims
189        self[ansi::NamedColor::DimForeground] =
190            colors.primary.dim_foreground.unwrap_or(colors.primary.foreground * 0.66);
191        match colors.dim {
192            Some(ref dim) => {
193                trace!("Using config-provided dim colors");
194                self[ansi::NamedColor::DimBlack] = dim.black;
195                self[ansi::NamedColor::DimRed] = dim.red;
196                self[ansi::NamedColor::DimGreen] = dim.green;
197                self[ansi::NamedColor::DimYellow] = dim.yellow;
198                self[ansi::NamedColor::DimBlue] = dim.blue;
199                self[ansi::NamedColor::DimMagenta] = dim.magenta;
200                self[ansi::NamedColor::DimCyan] = dim.cyan;
201                self[ansi::NamedColor::DimWhite] = dim.white;
202            }
203            None => {
204                trace!("Deriving dim colors from normal colors");
205                self[ansi::NamedColor::DimBlack] = colors.normal().black * 0.66;
206                self[ansi::NamedColor::DimRed] = colors.normal().red * 0.66;
207                self[ansi::NamedColor::DimGreen] = colors.normal().green * 0.66;
208                self[ansi::NamedColor::DimYellow] = colors.normal().yellow * 0.66;
209                self[ansi::NamedColor::DimBlue] = colors.normal().blue * 0.66;
210                self[ansi::NamedColor::DimMagenta] = colors.normal().magenta * 0.66;
211                self[ansi::NamedColor::DimCyan] = colors.normal().cyan * 0.66;
212                self[ansi::NamedColor::DimWhite] = colors.normal().white * 0.66;
213            }
214        }
215    }
216
217    pub fn fill_cube(&mut self, colors: &Colors) {
218        let mut index: usize = 16;
219        // Build colors
220        for r in 0..6 {
221            for g in 0..6 {
222                for b in 0..6 {
223                    // Override colors 16..232 with the config (if present)
224                    if let Some(indexed_color) =
225                        colors.indexed_colors.iter().find(|ic| ic.index == index as u8)
226                    {
227                        self[index] = indexed_color.color;
228                    } else {
229                        self[index] = Rgb {
230                            r: if r == 0 { 0 } else { r * 40 + 55 },
231                            b: if b == 0 { 0 } else { b * 40 + 55 },
232                            g: if g == 0 { 0 } else { g * 40 + 55 },
233                        };
234                    }
235                    index += 1;
236                }
237            }
238        }
239
240        debug_assert!(index == 232);
241    }
242
243    pub fn fill_gray_ramp(&mut self, colors: &Colors) {
244        let mut index: usize = 232;
245
246        for i in 0..24 {
247            // Index of the color is number of named colors + number of cube colors + i
248            let color_index = 16 + 216 + i;
249
250            // Override colors 232..256 with the config (if present)
251            if let Some(indexed_color) =
252                colors.indexed_colors.iter().find(|ic| ic.index == color_index)
253            {
254                self[index] = indexed_color.color;
255                index += 1;
256                continue;
257            }
258
259            let value = i * 10 + 8;
260            self[index] = Rgb { r: value, g: value, b: value };
261            index += 1;
262        }
263
264        debug_assert!(index == 256);
265    }
266}
267
268impl fmt::Debug for List {
269    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270        f.write_str("List[..]")
271    }
272}
273
274impl Index<ansi::NamedColor> for List {
275    type Output = Rgb;
276
277    #[inline]
278    fn index(&self, idx: ansi::NamedColor) -> &Self::Output {
279        &self.0[idx as usize]
280    }
281}
282
283impl IndexMut<ansi::NamedColor> for List {
284    #[inline]
285    fn index_mut(&mut self, idx: ansi::NamedColor) -> &mut Self::Output {
286        &mut self.0[idx as usize]
287    }
288}
289
290impl Index<usize> for List {
291    type Output = Rgb;
292
293    #[inline]
294    fn index(&self, idx: usize) -> &Self::Output {
295        &self.0[idx]
296    }
297}
298
299impl IndexMut<usize> for List {
300    #[inline]
301    fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
302        &mut self.0[idx]
303    }
304}
305
306impl Index<u8> for List {
307    type Output = Rgb;
308
309    #[inline]
310    fn index(&self, idx: u8) -> &Self::Output {
311        &self.0[idx as usize]
312    }
313}
314
315impl IndexMut<u8> for List {
316    #[inline]
317    fn index_mut(&mut self, idx: u8) -> &mut Self::Output {
318        &mut self.0[idx as usize]
319    }
320}