use serde::{Deserialize, Serialize}; #[derive(Clone, Debug)] pub struct Color { pub r: u8, pub g: u8, pub b: u8, } impl<'de> Deserialize<'de> for Color { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let buf = String::deserialize(deserializer)?; if buf.len() != 7 { return Err(serde::de::Error::custom("color must be 7 chars long")); } let mut chars = buf.chars().collect::>(); if chars.remove(0) != '#' { return Err(serde::de::Error::custom("color must start with #")); } let colors = chars .chunks(2) .map(|c| c.iter().collect::()) .map(|s| parse_color(&s)) .collect::, D::Error>>()?; Ok(Color { r: colors[0], g: colors[1], b: colors[2], }) } } impl Serialize for Color { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { let string = format!("#{:02X}{:02X}{:02X}", self.r, self.g, self.b); serializer.serialize_str(&string) } } fn parse_color(hex_code: &str) -> Result where E: serde::de::Error, { let res = u8::from_str_radix(hex_code, 16); if res.is_err() { return Err(E::custom("could not deserialize color")); } Ok(res.unwrap()) } impl Color { pub fn from_hex(hex: &str) -> Self { serde_json::from_str(&format!("\"{hex}\"")).unwrap() } } impl From<&str> for Color { fn from(value: &str) -> Self { Color::from_hex(value) } } #[cfg(test)] #[test] fn test_serialize_color() { let color = Color { r: 10, g: 20, b: 30, }; let serialized_color = serde_json::to_string(&color).unwrap(); assert_eq!(serialized_color, "\"#0A141E\""); } #[cfg(test)] #[test] fn test_deserialize_color() { let color = "\"#0A141E\""; let deserialized_color: Color = serde_json::from_str(color).unwrap(); assert_eq!(deserialized_color.r, 10); assert_eq!(deserialized_color.g, 20); assert_eq!(deserialized_color.b, 30); assert!(serde_json::from_str::("\"000000\"").is_err()); assert!(serde_json::from_str::("\"#00000\"").is_err()); assert!(serde_json::from_str::("\"#0000000\"").is_err()); assert!(serde_json::from_str::("\"#G00000\"").is_err()); }