color_to_tui/
lib.rs

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}