use serde::de::Visitor;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
const MAX_SAFE_INTEGER: u64 = (1 << 53) - 1;
pub(crate) mod u64 {
use super::*;
pub fn serialize<S: Serializer>(value: &u64, serializer: S) -> Result<S::Ok, S::Error> {
if *value <= MAX_SAFE_INTEGER {
serializer.serialize_u64(*value)
} else {
serializer.serialize_str(&value.to_string())
}
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u64, D::Error> {
struct UnsignedVisitor;
impl Visitor<'_> for UnsignedVisitor {
type Value = u64;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a decimal uint64 string or a safe JSON integer")
}
fn visit_u64<E: serde::de::Error>(self, value: u64) -> Result<Self::Value, E> {
(value <= MAX_SAFE_INTEGER)
.then_some(value)
.ok_or_else(|| E::custom("JSON integer exceeds the safe range"))
}
fn visit_i64<E: serde::de::Error>(self, value: i64) -> Result<Self::Value, E> {
u64::try_from(value)
.map_err(E::custom)
.and_then(|value| self.visit_u64(value))
}
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
if value.is_empty() || !value.bytes().all(|byte| byte.is_ascii_digit()) {
return Err(E::custom("uint64 string must contain decimal digits"));
}
value.parse().map_err(E::custom)
}
}
deserializer.deserialize_any(UnsignedVisitor)
}
}
struct SafeUnsigned(u64);
impl Serialize for SafeUnsigned {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
u64::serialize(&self.0, serializer)
}
}
impl<'de> Deserialize<'de> for SafeUnsigned {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
u64::deserialize(deserializer).map(Self)
}
}
pub mod option_u64 {
use super::*;
pub fn serialize<S: Serializer>(value: &Option<u64>, serializer: S) -> Result<S::Ok, S::Error> {
value.map(SafeUnsigned).serialize(serializer)
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<u64>, D::Error> {
Option::<SafeUnsigned>::deserialize(deserializer).map(|value| value.map(|safe| safe.0))
}
}
pub mod option_ranges {
use std::ops::Range;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
struct Bounds {
#[serde(with = "super::u64")]
start: u64,
#[serde(with = "super::u64")]
end: u64,
}
pub fn serialize<S: Serializer>(
value: &Option<Vec<Range<u64>>>,
serializer: S,
) -> Result<S::Ok, S::Error> {
value
.as_ref()
.map(|ranges| {
ranges
.iter()
.map(|range| Bounds {
start: range.start,
end: range.end,
})
.collect::<Vec<_>>()
})
.serialize(serializer)
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Option<Vec<Range<u64>>>, D::Error> {
Ok(
Option::<Vec<Bounds>>::deserialize(deserializer)?.map(|ranges| {
ranges
.into_iter()
.map(|bounds| bounds.start..bounds.end)
.collect()
}),
)
}
}
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
struct Counts {
#[serde(default, with = "super::option_u64")]
optional: Option<u64>,
}
#[test]
fn large_values_use_strings_and_unsafe_numbers_fail() {
let counts = Counts {
optional: Some(u64::MAX),
};
let encoded = serde_json::to_value(&counts).expect("encode counts");
assert_eq!(encoded, json!({"optional":"18446744073709551615"}));
assert_eq!(
serde_json::from_value::<Counts>(encoded).expect("decode counts"),
counts
);
assert!(
serde_json::from_value::<Counts>(json!({"optional":9007199254740992_u64})).is_err()
);
}
}