use std::fmt;
use serde::de::{self, Visitor};
use serde::{Deserializer, Serializer};
pub fn serialize<S: Serializer>(value: &u64, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
serializer.serialize_str(&format!("0x{value:x}"))
} else {
serializer.serialize_u64(*value)
}
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u64, D::Error> {
struct Wide;
impl Visitor<'_> for Wide {
type Value = u64;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an unsigned 64-bit integer or a hexadecimal string")
}
fn visit_u64<E: de::Error>(self, value: u64) -> Result<u64, E> {
Ok(value)
}
fn visit_i64<E: de::Error>(self, value: i64) -> Result<u64, E> {
u64::try_from(value).map_err(|_| E::custom("a negative number is not a u64"))
}
fn visit_str<E: de::Error>(self, text: &str) -> Result<u64, E> {
let digits = text
.strip_prefix("0x")
.or_else(|| text.strip_prefix("0X"))
.unwrap_or(text);
u64::from_str_radix(digits, 16)
.map_err(|_| E::custom(format!("{text:?} is not a hexadecimal integer")))
}
}
if deserializer.is_human_readable() {
deserializer.deserialize_any(Wide)
} else {
deserializer.deserialize_u64(Wide)
}
}
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Holder {
#[serde(with = "super")]
wide: u64,
}
#[test]
fn json_carries_wide_integers_as_hex_strings() {
let holder = Holder {
wide: 0x28ab_0000_c0ff_ee1d,
};
let json = serde_json::to_string(&holder).expect("serializes");
assert_eq!(json, r#"{"wide":"0x28ab0000c0ffee1d"}"#);
let back: Holder = serde_json::from_str(&json).expect("parses");
assert_eq!(back, holder);
}
#[test]
fn json_still_reads_numbers_and_bare_hex() {
let back: Holder = serde_json::from_str(r#"{"wide":40}"#).expect("parses");
assert_eq!(back.wide, 40);
let back: Holder = serde_json::from_str(r#"{"wide":"ff"}"#).expect("parses");
assert_eq!(back.wide, 255);
assert!(serde_json::from_str::<Holder>(r#"{"wide":"0xzz"}"#).is_err());
assert!(serde_json::from_str::<Holder>(r#"{"wide":-1}"#).is_err());
}
#[test]
fn zero_and_the_maximum_round_trip() {
for wide in [0, 1, u64::MAX] {
let json = serde_json::to_string(&Holder { wide }).expect("serializes");
let back: Holder = serde_json::from_str(&json).expect("parses");
assert_eq!(back.wide, wide);
}
}
}