// SPDX-License-Identifier: LGPL-2.1-or-later // See Notices.txt for copyright information use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; pub(crate) trait SerdeHex { fn serialize(&self, serializer: S) -> Result; fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result where Self: Sized; } #[derive(Deserialize, Serialize)] struct SerdeHexWrapper(#[serde(with = "SerdeHex")] T); fn serialize_ref_helper( v: &&T, serializer: S, ) -> Result { v.serialize(serializer) } #[derive(Serialize)] struct SerdeHexRefWrapper<'a, T: SerdeHex>(#[serde(serialize_with = "serialize_ref_helper")] &'a T); impl SerdeHex for Option { fn serialize(&self, serializer: S) -> Result { self.as_ref().map(SerdeHexRefWrapper).serialize(serializer) } fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result where Self: Sized, { Ok(Option::>::deserialize(deserializer)?.map(|v| v.0)) } } macro_rules! impl_hex_for_uint { ($ty:ty) => { impl SerdeHex for $ty { fn serialize(&self, serializer: S) -> Result { serializer.serialize_str(&format!("{:#X}", self)) } fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result { let text: &str = Deserialize::deserialize(deserializer)?; const PREFIX: &str = "0x"; if text.starts_with(PREFIX) { let hex_digits = &text[PREFIX.len()..]; Self::from_str_radix(hex_digits, 16).map_err(de::Error::custom) } else { Err(de::Error::custom("hexadecimal field must start with 0x")) } } } }; } impl_hex_for_uint!(u8); impl_hex_for_uint!(u16); impl_hex_for_uint!(u32); impl_hex_for_uint!(u64); impl_hex_for_uint!(u128);