Documentation
pub mod option_const_hex {
    use serde::{Deserialize, Deserializer, Serializer};

    pub fn serialize<S>(value: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match value {
            Some(bytes) => const_hex::serialize(bytes, serializer),
            None => serializer.serialize_none(),
        }
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
    where
        D: Deserializer<'de>,
    {
        Option::<String>::deserialize(deserializer)?
            .map(|s| const_hex::decode(&s).map_err(serde::de::Error::custom))
            .transpose()
    }
}

#[cfg(test)]
mod test {
    #[test]
    fn test_option_const_hex() {
        use super::option_const_hex;
        use serde::{Deserialize, Serialize};

        #[derive(Serialize, Deserialize, PartialEq, Debug)]
        struct TestStruct {
            #[serde(with = "option_const_hex")]
            data: Option<Vec<u8>>,
        }

        let original = TestStruct {
            data: Some(vec![1, 2, 3, 4, 5]),
        };

        let expected_str = const_hex::encode_prefixed(original.data.as_ref().unwrap());

        let serialized = serde_json::to_string(&original).unwrap();
        assert_eq!(serialized, format!("{{\"data\":\"{expected_str}\"}}"));

        let deserialized: TestStruct = serde_json::from_str(&serialized).unwrap();
        assert_eq!(deserialized, original);

        let original_none = TestStruct { data: None };
        let serialized_none = serde_json::to_string(&original_none).unwrap();
        assert_eq!(serialized_none, r#"{"data":null}"#);

        let deserialized_none: TestStruct = serde_json::from_str(&serialized_none).unwrap();
        assert_eq!(deserialized_none, original_none);
    }
}