1use std::{
2 convert::{AsRef, TryFrom},
3 str::FromStr,
4};
5
6#[cfg(feature = "serde")]
7use std::fmt;
8
9use 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)]
31pub 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
96impl 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
168impl 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
198impl 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
212impl 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")]
221impl 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")]
262impl<'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)]
331mod 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")]
376mod 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