1 | use std::{
|
2 | convert::{AsRef, TryFrom},
|
3 | str::FromStr,
|
4 | };
|
5 |
|
6 | #[cfg (feature = "serde" )]
|
7 | use std::fmt;
|
8 |
|
9 | use crate::style::parse_next_u8;
|
10 |
|
11 | /// Represents a color.
|
12 | ///
|
13 | /// # Platform-specific Notes
|
14 | ///
|
15 | /// The following list of 16 base colors are available for almost all terminals (Windows 7 and 8 included).
|
16 | ///
|
17 | /// | Light | Dark |
|
18 | /// | :--------- | :------------ |
|
19 | /// | `DarkGrey` | `Black` |
|
20 | /// | `Red` | `DarkRed` |
|
21 | /// | `Green` | `DarkGreen` |
|
22 | /// | `Yellow` | `DarkYellow` |
|
23 | /// | `Blue` | `DarkBlue` |
|
24 | /// | `Magenta` | `DarkMagenta` |
|
25 | /// | `Cyan` | `DarkCyan` |
|
26 | /// | `White` | `Grey` |
|
27 | ///
|
28 | /// Most UNIX terminals and Windows 10 consoles support additional colors.
|
29 | /// See [`Color::Rgb`] or [`Color::AnsiValue`] for more info.
|
30 | #[derive (Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
31 | pub enum Color {
|
32 | /// Resets the terminal color.
|
33 | Reset,
|
34 |
|
35 | /// Black color.
|
36 | Black,
|
37 |
|
38 | /// Dark grey color.
|
39 | DarkGrey,
|
40 |
|
41 | /// Light red color.
|
42 | Red,
|
43 |
|
44 | /// Dark red color.
|
45 | DarkRed,
|
46 |
|
47 | /// Light green color.
|
48 | Green,
|
49 |
|
50 | /// Dark green color.
|
51 | DarkGreen,
|
52 |
|
53 | /// Light yellow color.
|
54 | Yellow,
|
55 |
|
56 | /// Dark yellow color.
|
57 | DarkYellow,
|
58 |
|
59 | /// Light blue color.
|
60 | Blue,
|
61 |
|
62 | /// Dark blue color.
|
63 | DarkBlue,
|
64 |
|
65 | /// Light magenta color.
|
66 | Magenta,
|
67 |
|
68 | /// Dark magenta color.
|
69 | DarkMagenta,
|
70 |
|
71 | /// Light cyan color.
|
72 | Cyan,
|
73 |
|
74 | /// Dark cyan color.
|
75 | DarkCyan,
|
76 |
|
77 | /// White color.
|
78 | White,
|
79 |
|
80 | /// Grey color.
|
81 | Grey,
|
82 |
|
83 | /// An RGB color. See [RGB color model](https://en.wikipedia.org/wiki/RGB_color_model) for more info.
|
84 | ///
|
85 | /// Most UNIX terminals and Windows 10 supported only.
|
86 | /// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info.
|
87 | Rgb { r: u8, g: u8, b: u8 },
|
88 |
|
89 | /// An ANSI color. See [256 colors - cheat sheet](https://jonasjacek.github.io/colors/) for more info.
|
90 | ///
|
91 | /// Most UNIX terminals and Windows 10 supported only.
|
92 | /// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info.
|
93 | AnsiValue(u8),
|
94 | }
|
95 |
|
96 | impl Color {
|
97 | /// Parses an ANSI color sequence.
|
98 | ///
|
99 | /// # Examples
|
100 | ///
|
101 | /// ```
|
102 | /// use crossterm::style::Color;
|
103 | ///
|
104 | /// assert_eq!(Color::parse_ansi("5;0" ), Some(Color::Black));
|
105 | /// assert_eq!(Color::parse_ansi("5;26" ), Some(Color::AnsiValue(26)));
|
106 | /// assert_eq!(Color::parse_ansi("2;50;60;70" ), Some(Color::Rgb { r: 50, g: 60, b: 70 }));
|
107 | /// assert_eq!(Color::parse_ansi("invalid color" ), None);
|
108 | /// ```
|
109 | ///
|
110 | /// Currently, 3/4 bit color values aren't supported so return `None`.
|
111 | ///
|
112 | /// See also: [`Colored::parse_ansi`](crate::style::Colored::parse_ansi).
|
113 | pub fn parse_ansi(ansi: &str) -> Option<Self> {
|
114 | Self::parse_ansi_iter(&mut ansi.split(';' ))
|
115 | }
|
116 |
|
117 | /// The logic for parse_ansi, takes an iterator of the sequences terms (the numbers between the
|
118 | /// ';'). It's a separate function so it can be used by both Color::parse_ansi and
|
119 | /// colored::parse_ansi.
|
120 | /// Tested in Colored tests.
|
121 | pub(crate) fn parse_ansi_iter<'a>(values: &mut impl Iterator<Item = &'a str>) -> Option<Self> {
|
122 | let color = match parse_next_u8(values)? {
|
123 | // 8 bit colors: `5;<n>`
|
124 | 5 => {
|
125 | let n = parse_next_u8(values)?;
|
126 |
|
127 | use Color::*;
|
128 | [
|
129 | Black, // 0
|
130 | DarkRed, // 1
|
131 | DarkGreen, // 2
|
132 | DarkYellow, // 3
|
133 | DarkBlue, // 4
|
134 | DarkMagenta, // 5
|
135 | DarkCyan, // 6
|
136 | Grey, // 7
|
137 | DarkGrey, // 8
|
138 | Red, // 9
|
139 | Green, // 10
|
140 | Yellow, // 11
|
141 | Blue, // 12
|
142 | Magenta, // 13
|
143 | Cyan, // 14
|
144 | White, // 15
|
145 | ]
|
146 | .get(n as usize)
|
147 | .copied()
|
148 | .unwrap_or(Color::AnsiValue(n))
|
149 | }
|
150 |
|
151 | // 24 bit colors: `2;<r>;<g>;<b>`
|
152 | 2 => Color::Rgb {
|
153 | r: parse_next_u8(values)?,
|
154 | g: parse_next_u8(values)?,
|
155 | b: parse_next_u8(values)?,
|
156 | },
|
157 |
|
158 | _ => return None,
|
159 | };
|
160 | // If there's another value, it's unexpected so return None.
|
161 | if values.next().is_some() {
|
162 | return None;
|
163 | }
|
164 | Some(color)
|
165 | }
|
166 | }
|
167 |
|
168 | impl TryFrom<&str> for Color {
|
169 | type Error = ();
|
170 |
|
171 | /// Try to create a `Color` from the string representation. This returns an error if the string does not match.
|
172 | fn try_from(src: &str) -> Result<Self, Self::Error> {
|
173 | let src = src.to_lowercase();
|
174 |
|
175 | match src.as_ref() {
|
176 | "reset" => Ok(Color::Reset),
|
177 | "black" => Ok(Color::Black),
|
178 | "dark_grey" => Ok(Color::DarkGrey),
|
179 | "red" => Ok(Color::Red),
|
180 | "dark_red" => Ok(Color::DarkRed),
|
181 | "green" => Ok(Color::Green),
|
182 | "dark_green" => Ok(Color::DarkGreen),
|
183 | "yellow" => Ok(Color::Yellow),
|
184 | "dark_yellow" => Ok(Color::DarkYellow),
|
185 | "blue" => Ok(Color::Blue),
|
186 | "dark_blue" => Ok(Color::DarkBlue),
|
187 | "magenta" => Ok(Color::Magenta),
|
188 | "dark_magenta" => Ok(Color::DarkMagenta),
|
189 | "cyan" => Ok(Color::Cyan),
|
190 | "dark_cyan" => Ok(Color::DarkCyan),
|
191 | "white" => Ok(Color::White),
|
192 | "grey" => Ok(Color::Grey),
|
193 | _ => Err(()),
|
194 | }
|
195 | }
|
196 | }
|
197 |
|
198 | impl FromStr for Color {
|
199 | type Err = ();
|
200 |
|
201 | /// Creates a `Color` from the string representation.
|
202 | ///
|
203 | /// # Notes
|
204 | ///
|
205 | /// * Returns `Color::White` in case of an unknown color.
|
206 | /// * Does not return `Err` and you can safely unwrap.
|
207 | fn from_str(src: &str) -> Result<Self, Self::Err> {
|
208 | Ok(Color::try_from(src).unwrap_or(default:Color::White))
|
209 | }
|
210 | }
|
211 |
|
212 | impl From<(u8, u8, u8)> for Color {
|
213 | /// Creates a 'Color' from the tuple representation.
|
214 | fn from(val: (u8, u8, u8)) -> Self {
|
215 | let (r: u8, g: u8, b: u8) = val;
|
216 | Self::Rgb { r, g, b }
|
217 | }
|
218 | }
|
219 |
|
220 | #[cfg (feature = "serde" )]
|
221 | impl serde::ser::Serialize for Color {
|
222 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
223 | where
|
224 | S: serde::ser::Serializer,
|
225 | {
|
226 | let str = match *self {
|
227 | Color::Reset => "reset" ,
|
228 | Color::Black => "black" ,
|
229 | Color::DarkGrey => "dark_grey" ,
|
230 | Color::Red => "red" ,
|
231 | Color::DarkRed => "dark_red" ,
|
232 | Color::Green => "green" ,
|
233 | Color::DarkGreen => "dark_green" ,
|
234 | Color::Yellow => "yellow" ,
|
235 | Color::DarkYellow => "dark_yellow" ,
|
236 | Color::Blue => "blue" ,
|
237 | Color::DarkBlue => "dark_blue" ,
|
238 | Color::Magenta => "magenta" ,
|
239 | Color::DarkMagenta => "dark_magenta" ,
|
240 | Color::Cyan => "cyan" ,
|
241 | Color::DarkCyan => "dark_cyan" ,
|
242 | Color::White => "white" ,
|
243 | Color::Grey => "grey" ,
|
244 | _ => "" ,
|
245 | };
|
246 |
|
247 | if str.is_empty() {
|
248 | match *self {
|
249 | Color::AnsiValue(value) => serializer.serialize_str(&format!("ansi_({})" , value)),
|
250 | Color::Rgb { r, g, b } => {
|
251 | serializer.serialize_str(&format!("rgb_({},{},{})" , r, g, b))
|
252 | }
|
253 | _ => Err(serde::ser::Error::custom("Could not serialize enum type" )),
|
254 | }
|
255 | } else {
|
256 | serializer.serialize_str(str)
|
257 | }
|
258 | }
|
259 | }
|
260 |
|
261 | #[cfg (feature = "serde" )]
|
262 | impl<'de> serde::de::Deserialize<'de> for Color {
|
263 | fn deserialize<D>(deserializer: D) -> Result<Color, D::Error>
|
264 | where
|
265 | D: serde::de::Deserializer<'de>,
|
266 | {
|
267 | struct ColorVisitor;
|
268 | impl serde::de::Visitor<'_> for ColorVisitor {
|
269 | type Value = Color;
|
270 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
271 | formatter.write_str(
|
272 | "`reset`, `black`, `blue`, `dark_blue`, `cyan`, `dark_cyan`, `green`, `dark_green`, `grey`, `dark_grey`, `magenta`, `dark_magenta`, `red`, `dark_red`, `white`, `yellow`, `dark_yellow`, `ansi_(value)`, or `rgb_(r,g,b)` or `#rgbhex`" ,
|
273 | )
|
274 | }
|
275 | fn visit_str<E>(self, value: &str) -> Result<Color, E>
|
276 | where
|
277 | E: serde::de::Error,
|
278 | {
|
279 | if let Ok(c) = Color::try_from(value) {
|
280 | Ok(c)
|
281 | } else {
|
282 | if value.contains("ansi" ) {
|
283 | // strip away `ansi_(..)' and get the inner value between parenthesis.
|
284 | let results = value.replace("ansi_(" , "" ).replace(")" , "" );
|
285 |
|
286 | let ansi_val = results.parse::<u8>();
|
287 |
|
288 | if let Ok(ansi) = ansi_val {
|
289 | return Ok(Color::AnsiValue(ansi));
|
290 | }
|
291 | } else if value.contains("rgb" ) {
|
292 | // strip away `rgb_(..)' and get the inner values between parenthesis.
|
293 | let results = value
|
294 | .replace("rgb_(" , "" )
|
295 | .replace(")" , "" )
|
296 | .split(',' )
|
297 | .map(|x| x.to_string())
|
298 | .collect::<Vec<String>>();
|
299 |
|
300 | if results.len() == 3 {
|
301 | let r = results[0].parse::<u8>();
|
302 | let g = results[1].parse::<u8>();
|
303 | let b = results[2].parse::<u8>();
|
304 |
|
305 | if let (Ok(r), Ok(g), Ok(b)) = (r, g, b) {
|
306 | return Ok(Color::Rgb { r, g, b });
|
307 | }
|
308 | }
|
309 | } else if let Some(hex) = value.strip_prefix('#' ) {
|
310 | if hex.is_ascii() && hex.len() == 6 {
|
311 | let r = u8::from_str_radix(&hex[0..2], 16);
|
312 | let g = u8::from_str_radix(&hex[2..4], 16);
|
313 | let b = u8::from_str_radix(&hex[4..6], 16);
|
314 |
|
315 | if let (Ok(r), Ok(g), Ok(b)) = (r, g, b) {
|
316 | return Ok(Color::Rgb { r, g, b });
|
317 | }
|
318 | }
|
319 | }
|
320 |
|
321 | Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
|
322 | }
|
323 | }
|
324 | }
|
325 |
|
326 | deserializer.deserialize_str(ColorVisitor)
|
327 | }
|
328 | }
|
329 |
|
330 | #[cfg (test)]
|
331 | mod tests {
|
332 | use super::Color;
|
333 |
|
334 | #[test ]
|
335 | fn test_known_color_conversion() {
|
336 | assert_eq!("reset" .parse(), Ok(Color::Reset));
|
337 | assert_eq!("grey" .parse(), Ok(Color::Grey));
|
338 | assert_eq!("dark_grey" .parse(), Ok(Color::DarkGrey));
|
339 | assert_eq!("red" .parse(), Ok(Color::Red));
|
340 | assert_eq!("dark_red" .parse(), Ok(Color::DarkRed));
|
341 | assert_eq!("green" .parse(), Ok(Color::Green));
|
342 | assert_eq!("dark_green" .parse(), Ok(Color::DarkGreen));
|
343 | assert_eq!("yellow" .parse(), Ok(Color::Yellow));
|
344 | assert_eq!("dark_yellow" .parse(), Ok(Color::DarkYellow));
|
345 | assert_eq!("blue" .parse(), Ok(Color::Blue));
|
346 | assert_eq!("dark_blue" .parse(), Ok(Color::DarkBlue));
|
347 | assert_eq!("magenta" .parse(), Ok(Color::Magenta));
|
348 | assert_eq!("dark_magenta" .parse(), Ok(Color::DarkMagenta));
|
349 | assert_eq!("cyan" .parse(), Ok(Color::Cyan));
|
350 | assert_eq!("dark_cyan" .parse(), Ok(Color::DarkCyan));
|
351 | assert_eq!("white" .parse(), Ok(Color::White));
|
352 | assert_eq!("black" .parse(), Ok(Color::Black));
|
353 | }
|
354 |
|
355 | #[test ]
|
356 | fn test_unknown_color_conversion_yields_white() {
|
357 | assert_eq!("foo" .parse(), Ok(Color::White));
|
358 | }
|
359 |
|
360 | #[test ]
|
361 | fn test_know_rgb_color_conversion() {
|
362 | assert_eq!(Color::from((0, 0, 0)), Color::Rgb { r: 0, g: 0, b: 0 });
|
363 | assert_eq!(
|
364 | Color::from((255, 255, 255)),
|
365 | Color::Rgb {
|
366 | r: 255,
|
367 | g: 255,
|
368 | b: 255
|
369 | }
|
370 | );
|
371 | }
|
372 | }
|
373 |
|
374 | #[cfg (test)]
|
375 | #[cfg (feature = "serde" )]
|
376 | mod serde_tests {
|
377 | use super::Color;
|
378 | use serde_json;
|
379 |
|
380 | #[test ]
|
381 | fn test_deserial_known_color_conversion() {
|
382 | assert_eq!(
|
383 | serde_json::from_str::<Color>(" \"Reset \"" ).unwrap(),
|
384 | Color::Reset
|
385 | );
|
386 | assert_eq!(
|
387 | serde_json::from_str::<Color>(" \"reset \"" ).unwrap(),
|
388 | Color::Reset
|
389 | );
|
390 | assert_eq!(
|
391 | serde_json::from_str::<Color>(" \"Red \"" ).unwrap(),
|
392 | Color::Red
|
393 | );
|
394 | assert_eq!(
|
395 | serde_json::from_str::<Color>(" \"red \"" ).unwrap(),
|
396 | Color::Red
|
397 | );
|
398 | assert_eq!(
|
399 | serde_json::from_str::<Color>(" \"dark_red \"" ).unwrap(),
|
400 | Color::DarkRed
|
401 | );
|
402 | assert_eq!(
|
403 | serde_json::from_str::<Color>(" \"grey \"" ).unwrap(),
|
404 | Color::Grey
|
405 | );
|
406 | assert_eq!(
|
407 | serde_json::from_str::<Color>(" \"dark_grey \"" ).unwrap(),
|
408 | Color::DarkGrey
|
409 | );
|
410 | assert_eq!(
|
411 | serde_json::from_str::<Color>(" \"green \"" ).unwrap(),
|
412 | Color::Green
|
413 | );
|
414 | assert_eq!(
|
415 | serde_json::from_str::<Color>(" \"dark_green \"" ).unwrap(),
|
416 | Color::DarkGreen
|
417 | );
|
418 | assert_eq!(
|
419 | serde_json::from_str::<Color>(" \"yellow \"" ).unwrap(),
|
420 | Color::Yellow
|
421 | );
|
422 | assert_eq!(
|
423 | serde_json::from_str::<Color>(" \"dark_yellow \"" ).unwrap(),
|
424 | Color::DarkYellow
|
425 | );
|
426 | assert_eq!(
|
427 | serde_json::from_str::<Color>(" \"blue \"" ).unwrap(),
|
428 | Color::Blue
|
429 | );
|
430 | assert_eq!(
|
431 | serde_json::from_str::<Color>(" \"dark_blue \"" ).unwrap(),
|
432 | Color::DarkBlue
|
433 | );
|
434 | assert_eq!(
|
435 | serde_json::from_str::<Color>(" \"magenta \"" ).unwrap(),
|
436 | Color::Magenta
|
437 | );
|
438 | assert_eq!(
|
439 | serde_json::from_str::<Color>(" \"dark_magenta \"" ).unwrap(),
|
440 | Color::DarkMagenta
|
441 | );
|
442 | assert_eq!(
|
443 | serde_json::from_str::<Color>(" \"cyan \"" ).unwrap(),
|
444 | Color::Cyan
|
445 | );
|
446 | assert_eq!(
|
447 | serde_json::from_str::<Color>(" \"dark_cyan \"" ).unwrap(),
|
448 | Color::DarkCyan
|
449 | );
|
450 | assert_eq!(
|
451 | serde_json::from_str::<Color>(" \"white \"" ).unwrap(),
|
452 | Color::White
|
453 | );
|
454 | assert_eq!(
|
455 | serde_json::from_str::<Color>(" \"black \"" ).unwrap(),
|
456 | Color::Black
|
457 | );
|
458 | }
|
459 |
|
460 | #[test ]
|
461 | fn test_deserial_unknown_color_conversion() {
|
462 | assert!(serde_json::from_str::<Color>(" \"unknown \"" ).is_err());
|
463 | }
|
464 |
|
465 | #[test ]
|
466 | fn test_deserial_ansi_value() {
|
467 | assert_eq!(
|
468 | serde_json::from_str::<Color>(" \"ansi_(255) \"" ).unwrap(),
|
469 | Color::AnsiValue(255)
|
470 | );
|
471 | }
|
472 |
|
473 | #[test ]
|
474 | fn test_deserial_unvalid_ansi_value() {
|
475 | assert!(serde_json::from_str::<Color>(" \"ansi_(256) \"" ).is_err());
|
476 | assert!(serde_json::from_str::<Color>(" \"ansi_(-1) \"" ).is_err());
|
477 | }
|
478 |
|
479 | #[test ]
|
480 | fn test_deserial_rgb() {
|
481 | assert_eq!(
|
482 | serde_json::from_str::<Color>(" \"rgb_(255,255,255) \"" ).unwrap(),
|
483 | Color::from((255, 255, 255))
|
484 | );
|
485 | }
|
486 |
|
487 | #[test ]
|
488 | fn test_deserial_unvalid_rgb() {
|
489 | assert!(serde_json::from_str::<Color>(" \"rgb_(255,255,255,255) \"" ).is_err());
|
490 | assert!(serde_json::from_str::<Color>(" \"rgb_(256,255,255) \"" ).is_err());
|
491 | }
|
492 |
|
493 | #[test ]
|
494 | fn test_deserial_rgb_hex() {
|
495 | assert_eq!(
|
496 | serde_json::from_str::<Color>(" \"#ffffff \"" ).unwrap(),
|
497 | Color::from((255, 255, 255))
|
498 | );
|
499 | assert_eq!(
|
500 | serde_json::from_str::<Color>(" \"#FFFFFF \"" ).unwrap(),
|
501 | Color::from((255, 255, 255))
|
502 | );
|
503 | }
|
504 |
|
505 | #[test ]
|
506 | fn test_deserial_unvalid_rgb_hex() {
|
507 | assert!(serde_json::from_str::<Color>(" \"#FFFFFFFF \"" ).is_err());
|
508 | assert!(serde_json::from_str::<Color>(" \"#FFGFFF \"" ).is_err());
|
509 | // Ferris is 4 bytes so this will be considered the correct length.
|
510 | assert!(serde_json::from_str::<Color>(" \"#ff🦀 \"" ).is_err());
|
511 | }
|
512 | }
|
513 | |