use serde::de::{self, Deserializer, SeqAccess, Visitor};
use std::fmt;
struct U64OrStringVisitor;
impl<'de> Visitor<'de> for U64OrStringVisitor {
type Value = u64;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("u64 or string-encoded u64")
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<u64, E> {
Ok(v)
}
fn visit_i64<E: de::Error>(self, v: i64) -> Result<u64, E> {
u64::try_from(v).map_err(|_| E::custom(format!("negative value {v} cannot be u64")))
}
fn visit_f64<E: de::Error>(self, v: f64) -> Result<u64, E> {
if v >= 0.0 && v <= u64::MAX as f64 {
Ok(v as u64)
} else {
Err(E::custom(format!("f64 {v} out of u64 range")))
}
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<u64, E> {
v.parse().map_err(E::custom)
}
}
struct I64OrStringVisitor;
impl<'de> Visitor<'de> for I64OrStringVisitor {
type Value = i64;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("i64 or string-encoded i64")
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<i64, E> {
i64::try_from(v).map_err(|_| E::custom(format!("u64 {v} overflows i64")))
}
fn visit_i64<E: de::Error>(self, v: i64) -> Result<i64, E> {
Ok(v)
}
fn visit_f64<E: de::Error>(self, v: f64) -> Result<i64, E> {
if v >= i64::MIN as f64 && v <= i64::MAX as f64 {
Ok(v as i64)
} else {
Err(E::custom(format!("f64 {v} out of i64 range")))
}
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<i64, E> {
v.parse().map_err(E::custom)
}
}
pub fn deserialize_u64<'de, D: Deserializer<'de>>(d: D) -> Result<u64, D::Error> {
d.deserialize_any(U64OrStringVisitor)
}
pub fn deserialize_i64<'de, D: Deserializer<'de>>(d: D) -> Result<i64, D::Error> {
d.deserialize_any(I64OrStringVisitor)
}
pub fn deserialize_option_u64<'de, D: Deserializer<'de>>(d: D) -> Result<Option<u64>, D::Error> {
struct V;
impl<'de> Visitor<'de> for V {
type Value = Option<u64>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("null, u64, or string-encoded u64")
}
fn visit_unit<E: de::Error>(self) -> Result<Option<u64>, E> {
Ok(None)
}
fn visit_none<E: de::Error>(self) -> Result<Option<u64>, E> {
Ok(None)
}
fn visit_some<D2: Deserializer<'de>>(self, d: D2) -> Result<Option<u64>, D2::Error> {
deserialize_u64(d).map(Some)
}
}
d.deserialize_option(V)
}
pub fn deserialize_option_i64<'de, D: Deserializer<'de>>(d: D) -> Result<Option<i64>, D::Error> {
struct V;
impl<'de> Visitor<'de> for V {
type Value = Option<i64>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("null, i64, or string-encoded i64")
}
fn visit_unit<E: de::Error>(self) -> Result<Option<i64>, E> {
Ok(None)
}
fn visit_none<E: de::Error>(self) -> Result<Option<i64>, E> {
Ok(None)
}
fn visit_some<D2: Deserializer<'de>>(self, d: D2) -> Result<Option<i64>, D2::Error> {
deserialize_i64(d).map(Some)
}
}
d.deserialize_option(V)
}
pub fn deserialize_option_option_u64<'de, D: Deserializer<'de>>(
d: D,
) -> Result<Option<Option<u64>>, D::Error> {
struct V;
impl<'de> Visitor<'de> for V {
type Value = Option<Option<u64>>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("null, u64, or string-encoded u64")
}
fn visit_unit<E: de::Error>(self) -> Result<Option<Option<u64>>, E> {
Ok(Some(None))
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<Option<Option<u64>>, E> {
Ok(Some(Some(v)))
}
fn visit_i64<E: de::Error>(self, v: i64) -> Result<Option<Option<u64>>, E> {
u64::try_from(v)
.map(|v| Some(Some(v)))
.map_err(|_| E::custom(format!("negative value {v} cannot be u64")))
}
fn visit_f64<E: de::Error>(self, v: f64) -> Result<Option<Option<u64>>, E> {
if v >= 0.0 && v < (u64::MAX as f64) {
Ok(Some(Some(v as u64)))
} else {
Err(E::custom(format!("f64 {v} out of u64 range")))
}
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Option<Option<u64>>, E> {
v.parse().map(|v| Some(Some(v))).map_err(E::custom)
}
}
d.deserialize_any(V)
}
pub fn deserialize_option_option_i64<'de, D: Deserializer<'de>>(
d: D,
) -> Result<Option<Option<i64>>, D::Error> {
struct V;
impl<'de> Visitor<'de> for V {
type Value = Option<Option<i64>>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("null, i64, or string-encoded i64")
}
fn visit_unit<E: de::Error>(self) -> Result<Option<Option<i64>>, E> {
Ok(Some(None))
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<Option<Option<i64>>, E> {
i64::try_from(v)
.map(|v| Some(Some(v)))
.map_err(|_| E::custom(format!("u64 {v} overflows i64")))
}
fn visit_i64<E: de::Error>(self, v: i64) -> Result<Option<Option<i64>>, E> {
Ok(Some(Some(v)))
}
fn visit_f64<E: de::Error>(self, v: f64) -> Result<Option<Option<i64>>, E> {
Ok(Some(Some(v as i64)))
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Option<Option<i64>>, E> {
v.parse().map(|v| Some(Some(v))).map_err(E::custom)
}
}
d.deserialize_any(V)
}
pub fn deserialize_option_vec_u64<'de, D: Deserializer<'de>>(
d: D,
) -> Result<Option<Vec<u64>>, D::Error> {
struct V;
impl<'de> Visitor<'de> for V {
type Value = Option<Vec<u64>>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("null or array of u64/string-encoded u64")
}
fn visit_unit<E: de::Error>(self) -> Result<Option<Vec<u64>>, E> {
Ok(None)
}
fn visit_none<E: de::Error>(self) -> Result<Option<Vec<u64>>, E> {
Ok(None)
}
fn visit_some<D2: Deserializer<'de>>(self, d: D2) -> Result<Option<Vec<u64>>, D2::Error> {
struct SeqV;
impl<'de> Visitor<'de> for SeqV {
type Value = Vec<u64>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("array of u64/string-encoded u64")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Vec<u64>, A::Error> {
let mut vec = Vec::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(elem) = seq.next_element::<serde_json::Value>()? {
let n = match &elem {
serde_json::Value::Number(n) => n
.as_u64()
.or_else(|| n.as_i64().and_then(|i| u64::try_from(i).ok()))
.ok_or_else(|| {
de::Error::custom(format!("cannot convert {n} to u64"))
})?,
serde_json::Value::String(s) => {
s.parse::<u64>().map_err(de::Error::custom)?
}
other => {
return Err(de::Error::custom(format!(
"expected number or string in array, got {other}"
)));
}
};
vec.push(n);
}
Ok(vec)
}
}
d.deserialize_seq(SeqV).map(Some)
}
}
d.deserialize_option(V)
}
pub fn deserialize_option_option_vec_u64<'de, D: Deserializer<'de>>(
d: D,
) -> Result<Option<Option<Vec<u64>>>, D::Error> {
struct V;
impl<'de> Visitor<'de> for V {
type Value = Option<Option<Vec<u64>>>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("null or array of u64/string-encoded u64")
}
fn visit_unit<E: de::Error>(self) -> Result<Option<Option<Vec<u64>>>, E> {
Ok(Some(None))
}
fn visit_seq<A: SeqAccess<'de>>(
self,
mut seq: A,
) -> Result<Option<Option<Vec<u64>>>, A::Error> {
let mut vec = Vec::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(elem) = seq.next_element::<serde_json::Value>()? {
let n = match &elem {
serde_json::Value::Number(n) => n
.as_u64()
.or_else(|| n.as_i64().and_then(|i| u64::try_from(i).ok()))
.ok_or_else(|| de::Error::custom(format!("cannot convert {n} to u64")))?,
serde_json::Value::String(s) => s.parse::<u64>().map_err(de::Error::custom)?,
other => {
return Err(de::Error::custom(format!(
"expected number or string in array, got {other}"
)));
}
};
vec.push(n);
}
Ok(Some(Some(vec)))
}
}
d.deserialize_any(V)
}
pub fn deserialize_option_vec_i64<'de, D: Deserializer<'de>>(
d: D,
) -> Result<Option<Vec<i64>>, D::Error> {
struct V;
impl<'de> Visitor<'de> for V {
type Value = Option<Vec<i64>>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("null or array of i64/string-encoded i64")
}
fn visit_unit<E: de::Error>(self) -> Result<Option<Vec<i64>>, E> {
Ok(None)
}
fn visit_none<E: de::Error>(self) -> Result<Option<Vec<i64>>, E> {
Ok(None)
}
fn visit_some<D2: Deserializer<'de>>(self, d: D2) -> Result<Option<Vec<i64>>, D2::Error> {
struct SeqV;
impl<'de> Visitor<'de> for SeqV {
type Value = Vec<i64>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("array of i64/string-encoded i64")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Vec<i64>, A::Error> {
let mut vec = Vec::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(elem) = seq.next_element::<serde_json::Value>()? {
let n = match &elem {
serde_json::Value::Number(n) => n.as_i64().ok_or_else(|| {
de::Error::custom(format!("cannot convert {n} to i64"))
})?,
serde_json::Value::String(s) => {
s.parse::<i64>().map_err(de::Error::custom)?
}
other => {
return Err(de::Error::custom(format!(
"expected number or string in array, got {other}"
)));
}
};
vec.push(n);
}
Ok(vec)
}
}
d.deserialize_seq(SeqV).map(Some)
}
}
d.deserialize_option(V)
}
pub fn deserialize_option_option_vec_i64<'de, D: Deserializer<'de>>(
d: D,
) -> Result<Option<Option<Vec<i64>>>, D::Error> {
struct V;
impl<'de> Visitor<'de> for V {
type Value = Option<Option<Vec<i64>>>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("null or array of i64/string-encoded i64")
}
fn visit_unit<E: de::Error>(self) -> Result<Option<Option<Vec<i64>>>, E> {
Ok(Some(None))
}
fn visit_seq<A: SeqAccess<'de>>(
self,
mut seq: A,
) -> Result<Option<Option<Vec<i64>>>, A::Error> {
let mut vec = Vec::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(elem) = seq.next_element::<serde_json::Value>()? {
let n = match &elem {
serde_json::Value::Number(n) => n
.as_i64()
.ok_or_else(|| de::Error::custom(format!("cannot convert {n} to i64")))?,
serde_json::Value::String(s) => s.parse::<i64>().map_err(de::Error::custom)?,
other => {
return Err(de::Error::custom(format!(
"expected number or string in array, got {other}"
)));
}
};
vec.push(n);
}
Ok(Some(Some(vec)))
}
}
d.deserialize_any(V)
}
fn narrow_opt<W, N, E: de::Error>(opt: Option<W>) -> Result<Option<N>, E>
where
N: TryFrom<W>,
N::Error: fmt::Display,
{
opt.map(|v| N::try_from(v).map_err(E::custom)).transpose()
}
fn narrow_opt_opt<W, N, E: de::Error>(opt: Option<Option<W>>) -> Result<Option<Option<N>>, E>
where
N: TryFrom<W>,
N::Error: fmt::Display,
{
match opt {
None => Ok(None),
Some(None) => Ok(Some(None)),
Some(Some(v)) => N::try_from(v).map(|n| Some(Some(n))).map_err(E::custom),
}
}
fn narrow_opt_vec<W, N, E: de::Error>(opt: Option<Vec<W>>) -> Result<Option<Vec<N>>, E>
where
N: TryFrom<W>,
N::Error: fmt::Display,
{
opt.map(|vec| {
vec.into_iter()
.map(|v| N::try_from(v).map_err(E::custom))
.collect()
})
.transpose()
}
fn narrow_opt_opt_vec<W, N, E: de::Error>(
opt: Option<Option<Vec<W>>>,
) -> Result<Option<Option<Vec<N>>>, E>
where
N: TryFrom<W>,
N::Error: fmt::Display,
{
match opt {
None => Ok(None),
Some(None) => Ok(Some(None)),
Some(Some(vec)) => vec
.into_iter()
.map(|v| N::try_from(v).map_err(E::custom))
.collect::<Result<Vec<N>, E>>()
.map(|v| Some(Some(v))),
}
}
pub fn deserialize_option_u32<'de, D: Deserializer<'de>>(d: D) -> Result<Option<u32>, D::Error> {
narrow_opt(deserialize_option_u64(d)?)
}
pub fn deserialize_option_i32<'de, D: Deserializer<'de>>(d: D) -> Result<Option<i32>, D::Error> {
narrow_opt(deserialize_option_i64(d)?)
}
pub fn deserialize_option_option_u32<'de, D: Deserializer<'de>>(
d: D,
) -> Result<Option<Option<u32>>, D::Error> {
narrow_opt_opt(deserialize_option_option_u64(d)?)
}
pub fn deserialize_option_option_i32<'de, D: Deserializer<'de>>(
d: D,
) -> Result<Option<Option<i32>>, D::Error> {
narrow_opt_opt(deserialize_option_option_i64(d)?)
}
pub fn deserialize_option_vec_u32<'de, D: Deserializer<'de>>(
d: D,
) -> Result<Option<Vec<u32>>, D::Error> {
narrow_opt_vec(deserialize_option_vec_u64(d)?)
}
pub fn deserialize_option_vec_i32<'de, D: Deserializer<'de>>(
d: D,
) -> Result<Option<Vec<i32>>, D::Error> {
narrow_opt_vec(deserialize_option_vec_i64(d)?)
}
pub fn deserialize_option_option_vec_u32<'de, D: Deserializer<'de>>(
d: D,
) -> Result<Option<Option<Vec<u32>>>, D::Error> {
narrow_opt_opt_vec(deserialize_option_option_vec_u64(d)?)
}
pub fn deserialize_option_option_vec_i32<'de, D: Deserializer<'de>>(
d: D,
) -> Result<Option<Option<Vec<i32>>>, D::Error> {
narrow_opt_opt_vec(deserialize_option_option_vec_i64(d)?)
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
#[derive(Deserialize, Debug, PartialEq)]
struct TestBare {
#[serde(deserialize_with = "deserialize_u64")]
balance: u64,
#[serde(deserialize_with = "deserialize_i64")]
timestamp: i64,
}
#[derive(Deserialize, Debug, PartialEq)]
struct TestOption {
#[serde(default, deserialize_with = "deserialize_option_u64")]
balance: Option<u64>,
#[serde(default, deserialize_with = "deserialize_option_i64")]
timestamp: Option<i64>,
}
#[derive(Deserialize, Debug, PartialEq)]
struct TestOptionOption {
#[serde(default, deserialize_with = "deserialize_option_option_u64")]
balance: Option<Option<u64>>,
#[serde(default, deserialize_with = "deserialize_option_option_i64")]
timestamp: Option<Option<i64>>,
}
#[derive(Deserialize, Debug, PartialEq)]
struct TestVec {
#[serde(default, deserialize_with = "deserialize_option_vec_u64")]
values: Option<Vec<u64>>,
}
#[derive(Deserialize, Debug, PartialEq)]
struct TestOptionOptionVec {
#[serde(default, deserialize_with = "deserialize_option_option_vec_u64")]
values: Option<Option<Vec<u64>>>,
}
#[derive(Deserialize, Debug, PartialEq)]
struct TestVecI64 {
#[serde(default, deserialize_with = "deserialize_option_vec_i64")]
values: Option<Vec<i64>>,
}
#[derive(Deserialize, Debug, PartialEq)]
struct TestOptionOptionVecI64 {
#[serde(default, deserialize_with = "deserialize_option_option_vec_i64")]
values: Option<Option<Vec<i64>>>,
}
#[test]
fn bare_u64_from_number() {
let v: TestBare = serde_json::from_str(r#"{"balance": 42, "timestamp": -100}"#).unwrap();
assert_eq!(v.balance, 42);
assert_eq!(v.timestamp, -100);
}
#[test]
fn bare_u64_from_string() {
let v: TestBare =
serde_json::from_str(r#"{"balance": "9007199254740992", "timestamp": "-100"}"#)
.unwrap();
assert_eq!(v.balance, 9007199254740992);
assert_eq!(v.timestamp, -100);
}
#[test]
fn option_from_number() {
let v: TestOption = serde_json::from_str(r#"{"balance": 42, "timestamp": -100}"#).unwrap();
assert_eq!(v.balance, Some(42));
assert_eq!(v.timestamp, Some(-100));
}
#[test]
fn option_from_string() {
let v: TestOption =
serde_json::from_str(r#"{"balance": "9007199254740992", "timestamp": "123"}"#).unwrap();
assert_eq!(v.balance, Some(9007199254740992));
assert_eq!(v.timestamp, Some(123));
}
#[test]
fn option_from_null() {
let v: TestOption =
serde_json::from_str(r#"{"balance": null, "timestamp": null}"#).unwrap();
assert_eq!(v.balance, None);
assert_eq!(v.timestamp, None);
}
#[test]
fn option_missing_field() {
let v: TestOption = serde_json::from_str(r#"{}"#).unwrap();
assert_eq!(v.balance, None);
assert_eq!(v.timestamp, None);
}
#[test]
fn option_option_from_number() {
let v: TestOptionOption =
serde_json::from_str(r#"{"balance": 42, "timestamp": -100}"#).unwrap();
assert_eq!(v.balance, Some(Some(42)));
assert_eq!(v.timestamp, Some(Some(-100)));
}
#[test]
fn option_option_from_string() {
let v: TestOptionOption =
serde_json::from_str(r#"{"balance": "9007199254740992", "timestamp": "123"}"#).unwrap();
assert_eq!(v.balance, Some(Some(9007199254740992)));
assert_eq!(v.timestamp, Some(Some(123)));
}
#[test]
fn option_option_null_means_explicit_null() {
let v: TestOptionOption =
serde_json::from_str(r#"{"balance": null, "timestamp": null}"#).unwrap();
assert_eq!(v.balance, Some(None)); assert_eq!(v.timestamp, Some(None));
}
#[test]
fn option_option_missing_means_not_received() {
let v: TestOptionOption = serde_json::from_str(r#"{}"#).unwrap();
assert_eq!(v.balance, None); assert_eq!(v.timestamp, None);
}
#[test]
fn vec_mixed_numbers_and_strings() {
let v: TestVec = serde_json::from_str(r#"{"values": [1, "9007199254740992", 3]}"#).unwrap();
assert_eq!(v.values, Some(vec![1, 9007199254740992, 3]));
}
#[test]
fn vec_null() {
let v: TestVec = serde_json::from_str(r#"{"values": null}"#).unwrap();
assert_eq!(v.values, None);
}
#[test]
fn vec_missing() {
let v: TestVec = serde_json::from_str(r#"{}"#).unwrap();
assert_eq!(v.values, None);
}
#[test]
fn option_option_vec_from_array() {
let v: TestOptionOptionVec =
serde_json::from_str(r#"{"values": [1, "9007199254740992"]}"#).unwrap();
assert_eq!(v.values, Some(Some(vec![1, 9007199254740992])));
}
#[test]
fn option_option_vec_null() {
let v: TestOptionOptionVec = serde_json::from_str(r#"{"values": null}"#).unwrap();
assert_eq!(v.values, Some(None));
}
#[test]
fn option_option_vec_missing() {
let v: TestOptionOptionVec = serde_json::from_str(r#"{}"#).unwrap();
assert_eq!(v.values, None);
}
#[test]
fn vec_i64_mixed_numbers_and_strings() {
let v: TestVecI64 =
serde_json::from_str(r#"{"values": [-1, "9007199254740992", 3]}"#).unwrap();
assert_eq!(v.values, Some(vec![-1, 9007199254740992, 3]));
}
#[test]
fn vec_i64_null() {
let v: TestVecI64 = serde_json::from_str(r#"{"values": null}"#).unwrap();
assert_eq!(v.values, None);
}
#[test]
fn vec_i64_missing() {
let v: TestVecI64 = serde_json::from_str(r#"{}"#).unwrap();
assert_eq!(v.values, None);
}
#[test]
fn option_option_vec_i64_from_array() {
let v: TestOptionOptionVecI64 =
serde_json::from_str(r#"{"values": [-1, "9007199254740992"]}"#).unwrap();
assert_eq!(v.values, Some(Some(vec![-1, 9007199254740992])));
}
#[test]
fn option_option_vec_i64_null() {
let v: TestOptionOptionVecI64 = serde_json::from_str(r#"{"values": null}"#).unwrap();
assert_eq!(v.values, Some(None));
}
#[test]
fn option_option_vec_i64_missing() {
let v: TestOptionOptionVecI64 = serde_json::from_str(r#"{}"#).unwrap();
assert_eq!(v.values, None);
}
#[test]
fn large_u64_from_string() {
let v: TestOption = serde_json::from_str(r#"{"balance": "18446744073709551615"}"#).unwrap();
assert_eq!(v.balance, Some(u64::MAX));
}
#[test]
fn u64_from_float() {
let v: TestOption = serde_json::from_str(r#"{"balance": 42.0}"#).unwrap();
assert_eq!(v.balance, Some(42));
}
#[derive(Deserialize, Debug, PartialEq)]
struct TestOption32 {
#[serde(default, deserialize_with = "deserialize_option_u32")]
balance: Option<u32>,
#[serde(default, deserialize_with = "deserialize_option_i32")]
timestamp: Option<i32>,
}
#[test]
fn option_u32_from_number() {
let v: TestOption32 =
serde_json::from_str(r#"{"balance": 42, "timestamp": -100}"#).unwrap();
assert_eq!(v.balance, Some(42));
assert_eq!(v.timestamp, Some(-100));
}
#[test]
fn option_u32_from_string() {
let v: TestOption32 =
serde_json::from_str(r#"{"balance": "1000", "timestamp": "-50"}"#).unwrap();
assert_eq!(v.balance, Some(1000));
assert_eq!(v.timestamp, Some(-50));
}
#[test]
fn option_u32_overflow_rejected() {
let r = serde_json::from_str::<TestOption32>(r#"{"balance": 4294967296}"#);
assert!(r.is_err(), "u32 overflow should be rejected");
}
#[test]
fn option_i32_overflow_rejected() {
let r = serde_json::from_str::<TestOption32>(r#"{"timestamp": 2147483648}"#);
assert!(r.is_err(), "i32 overflow should be rejected");
}
#[test]
fn option_u32_null_and_missing() {
let v: TestOption32 =
serde_json::from_str(r#"{"balance": null, "timestamp": null}"#).unwrap();
assert_eq!(v.balance, None);
assert_eq!(v.timestamp, None);
let v: TestOption32 = serde_json::from_str(r#"{}"#).unwrap();
assert_eq!(v.balance, None);
assert_eq!(v.timestamp, None);
}
#[derive(Deserialize, Debug, PartialEq)]
struct TestOptionOption32 {
#[serde(default, deserialize_with = "deserialize_option_option_u32")]
balance: Option<Option<u32>>,
}
#[test]
fn option_option_u32_patch_semantics() {
let v: TestOptionOption32 = serde_json::from_str(r#"{"balance": 42}"#).unwrap();
assert_eq!(v.balance, Some(Some(42)));
let v: TestOptionOption32 = serde_json::from_str(r#"{"balance": null}"#).unwrap();
assert_eq!(v.balance, Some(None));
let v: TestOptionOption32 = serde_json::from_str(r#"{}"#).unwrap();
assert_eq!(v.balance, None);
}
#[derive(Deserialize, Debug, PartialEq)]
struct TestVec32 {
#[serde(default, deserialize_with = "deserialize_option_vec_u32")]
values: Option<Vec<u32>>,
}
#[test]
fn vec_u32_mixed() {
let v: TestVec32 = serde_json::from_str(r#"{"values": [1, "2", 3]}"#).unwrap();
assert_eq!(v.values, Some(vec![1, 2, 3]));
}
#[test]
fn vec_u32_overflow_rejected() {
let r = serde_json::from_str::<TestVec32>(r#"{"values": [1, 4294967296]}"#);
assert!(r.is_err());
}
}