hexcolor 0.1.0

A tiny HexColor type with serde support
Documentation
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HexColor {
    pub r: u8,
    pub g: u8,
    pub b: u8,
}

impl HexColor {
    pub fn new(r: u8, g: u8, b: u8) -> Self {
        Self { r, g, b }
    }

    pub fn parse(s: &str) -> Result<Self, &'static str> {
        let s = s.strip_prefix('#').ok_or("missing '#' prefix")?;

        if s.len() != 6 {
            return Err("expected 6 hex digits");
        }

        let r = u8::from_str_radix(&s[0..2], 16).map_err(|_| "invalid red")?;
        let g = u8::from_str_radix(&s[2..4], 16).map_err(|_| "invalid green")?;
        let b = u8::from_str_radix(&s[4..6], 16).map_err(|_| "invalid blue")?;

        Ok(Self { r, g, b })
    }

    pub fn to_hex_string(&self) -> String {
        format!("#{:02x}{:02x}{:02x}", self.r, self.g, self.b)
    }
}

impl std::fmt::Display for HexColor {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.to_hex_string())
    }
}

#[cfg(feature = "serde")]
mod serde_impl {
    use super::HexColor;
    use serde::{Deserialize, Deserializer, Serialize, Serializer};

    impl<'de> Deserialize<'de> for HexColor {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
        {
            let s = String::deserialize(deserializer)?;
            HexColor::parse(&s).map_err(serde::de::Error::custom)
        }
    }

    impl Serialize for HexColor {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            serializer.serialize_str(&self.to_hex_string())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let c = HexColor::parse("#532746").unwrap();
        assert_eq!(c.to_hex_string(), "#532746");
    }
}