use std::ops::Range;
use serde::{de::Unexpected, Deserialize, Deserializer, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum Color {
Transparent,
Rgb { r: u8, g: u8, b: u8 },
}
impl Color {
pub(crate) fn from_hex_str<'de, D>(deserializer: D) -> Result<Option<Self>, D::Error>
where
D: Deserializer<'de>,
{
fn get_rgb_value<'de, D: Deserializer<'de>>(
hex: &str,
range: Range<usize>,
) -> Result<u8, <D as Deserializer<'de>>::Error> {
use serde::de::Error;
let value = if let Some(value) = hex
.get(range)
.and_then(|val| u8::from_str_radix(val, 16).ok())
{
value
} else {
return Err(D::Error::invalid_value(
Unexpected::Other("non-hex string"),
&"hex string of format #RRGGBB",
));
};
Ok(value)
}
let hex = match Option::<String>::deserialize(deserializer) {
Ok(Some(s)) if s == "transparent" => return Ok(Some(Self::Transparent)),
Ok(Some(hex)) => hex,
Ok(None) => return Ok(None),
Err(why) => return Err(why),
};
if hex.is_empty() {
return Ok(None);
}
Ok(Some(Self::Rgb {
r: get_rgb_value::<D>(&hex, 1..3)?,
g: get_rgb_value::<D>(&hex, 3..5)?,
b: get_rgb_value::<D>(&hex, 5..7)?,
}))
}
}