1#[cfg(feature = "optional")]
2pub mod optional;
3
4use serde::{Deserialize, Deserializer, Serializer};
5use tui::style::Color;
6
7pub fn serialize<S: Serializer>(color: &Color, serializer: S) -> Result<S::Ok, S::Error> {
8 serializer.serialize_str(&match color {
9 Color::Reset => "Reset".to_string(),
10 Color::Red => "Red".to_string(),
11 Color::Green => "Green".to_string(),
12 Color::Black => "Black".to_string(),
13 Color::Yellow => "Yellow".to_string(),
14 Color::Blue => "Blue".to_string(),
15 Color::Magenta => "Magenta".to_string(),
16 Color::Cyan => "Cyan".to_string(),
17 Color::Gray => "Gray".to_string(),
18 Color::White => "White".to_string(),
19
20 Color::DarkGray => "DarkGray".to_string(),
21 Color::LightBlue => "LightBlue".to_string(),
22 Color::LightCyan => "LightCyan".to_string(),
23 Color::LightGreen => "LightGreen".to_string(),
24 Color::LightMagenta => "LightMagenta".to_string(),
25 Color::LightRed => "LightRed".to_string(),
26 Color::LightYellow => "LightYellow".to_string(),
27 Color::Indexed(index) => format!("{:03}", index),
28 Color::Rgb(r, g, b) => format!("#{:02X}{:02X}{:02X}", r, g, b),
29 })
30}
31
32pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Color, D::Error> {
33 use serde::de::{Error, Unexpected};
34
35 let color_string = String::deserialize(deserializer)?;
36 Ok(match color_string.to_lowercase().as_str() {
37 "reset" => Color::Reset,
38 "red" => Color::Red,
39 "green" => Color::Green,
40 "black" => Color::Black,
41 "yellow" => Color::Yellow,
42 "blue" => Color::Blue,
43 "magenta" => Color::Magenta,
44 "cyan" => Color::Cyan,
45 "gray" => Color::Gray,
46 "white" => Color::White,
47
48 "darkgray" => Color::DarkGray,
49 "lightblue" => Color::LightBlue,
50 "lightcyan" => Color::LightCyan,
51 "lightgreen" => Color::LightGreen,
52 "lightmagenta" => Color::LightMagenta,
53 "lightred" => Color::LightRed,
54 "lightyellow" => Color::LightYellow,
55 _ => match color_string.len() {
56 3 => {
57 let index = color_string.parse::<u8>();
58 if let Ok(index) = index {
59 Color::Indexed(index)
60 } else {
61 return Err(Error::invalid_type(
62 Unexpected::Bytes(color_string.as_bytes()),
63 &"u8 index color",
64 ));
65 }
66 }
67 4 | 7 => {
68 if !color_string.starts_with('#') {
69 return Err(Error::invalid_value(
70 Unexpected::Char(color_string.chars().next().unwrap()),
71 &"# at the start",
72 ));
73 }
74
75 let color_string = color_string.trim_start_matches('#');
76
77 let (r, g, b);
78
79 match color_string.len() {
80 6 => {
81 r = u8::from_str_radix(&color_string[0..2], 16);
82 g = u8::from_str_radix(&color_string[2..4], 16);
83 b = u8::from_str_radix(&color_string[4..6], 16);
84 }
85 3 => {
86 r = u8::from_str_radix(&color_string[0..1], 16).map(|r| r * 17);
87 g = u8::from_str_radix(&color_string[1..2], 16).map(|g| g * 17);
88 b = u8::from_str_radix(&color_string[2..3], 16).map(|b| b * 17);
89 }
90 _ => unreachable!("Can't be reached since already checked"),
91 }
92
93 match (r, g, b) {
94 (Ok(r), Ok(g), Ok(b)) => Color::Rgb(r, g, b),
95 (_, _, _) => {
96 return Err(Error::invalid_value(
97 Unexpected::Bytes(color_string.as_bytes()),
98 &"hex color string",
99 ));
100 }
101 }
102 }
103 _ => {
104 return Err(serde::de::Error::invalid_length(
105 color_string.len(),
106 &"color string with length 4 or 7",
107 ))
108 }
109 },
110 })
111}
112
113#[cfg(test)]
114mod tests {
115 use serde::{Deserialize, Serialize};
116 use tui::style::Color;
117
118 #[derive(Debug, PartialEq, Serialize, Deserialize)]
119 struct Test {
120 #[serde(with = "super")]
121 pub c: Color,
122 }
123
124 #[test]
125 fn serialize_index() {
126 let color: Color = Color::Indexed(123);
127 let t = Test { c: color };
128 let color_string = serde_json::to_string(&t).unwrap();
129 assert_eq!(color_string, r###"{"c":"123"}"###);
130 }
131
132 #[test]
133 fn serialize_hex() {
134 let color: Color = Color::Rgb(18, 252, 28);
135 let t = Test { c: color };
136 let color_string = serde_json::to_string(&t).unwrap();
137 assert_eq!(color_string, r###"{"c":"#12FC1C"}"###);
138 }
139
140 #[test]
141 fn deserialize_hex() {
142 let color: Color = Color::Rgb(18, 252, 28);
143 let color_text = r###"{ "c": "#12fc1c" }"###;
144 let t: Test = serde_json::from_str::<Test>(color_text).unwrap();
145 assert_eq!(t, Test { c: color });
146 }
147
148 #[test]
149 fn deserialize_short_hex() {
150 let color: Color = Color::Rgb(255, 255, 170);
151 let color_text = r###"{ "c": "#FFA" }"###;
152 let t: Test = serde_json::from_str::<Test>(color_text).unwrap();
153 assert_eq!(t, Test { c: color });
154 }
155
156 #[test]
157 fn deserialize_hex_and_short_hex() {
158 let color_text_hex = r###"{ "c": "#FF99CC" }"###;
159 let color_text_short_hex = r###"{ "c": "#F9C" }"###;
160 let t_h: Test = serde_json::from_str::<Test>(color_text_hex).unwrap();
161 let t_sh: Test = serde_json::from_str::<Test>(color_text_short_hex).unwrap();
162 assert_eq!(t_h, t_sh);
163 }
164
165 #[test]
166 fn deserialize_index() {
167 let color: Color = Color::Indexed(123);
168 let color_text = r###"{ "c": "123" }"###;
169 let t: Test = serde_json::from_str::<Test>(color_text).unwrap();
170 assert_eq!(t, Test { c: color });
171 }
172}