I have two custom functions used for serde deserialize_with
// Converts string to u32
pub fn de_u32_from_str<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse::<u32>().map_err(de::Error::custom)
}
// Converts string that may be `null` to either `Some(u32)` or `None`
pub fn de_opt_u32_from_opt_str<'de, D>(deserializer: D) -> Result<Option<u32>, D::Error>
where
D: Deserializer<'de>,
{
let s = Option::<String>::deserialize(deserializer)?;
s.map(|s| s.parse::<u32>().map_err(de::Error::custom))
.transpose()
}
I tested de_u32_from_str
like this:
fn deserialize_u32_from_str(string: &str, expected: u32) {
let de: StrDeserializer<ValueError> = string.into_deserializer();
let result = de_u32_from_str(de).unwrap();
assert_eq!(result, expected);
}
However I struggle creating deserializer
for testing de_opt_u32_from_opt_str
fn deserialize_opt_u32_from_opt_str(maybe_string: Option<&str>, expected: Option<u32>) {
// I need a `deserializer` here
let de: ... = ...;
let result = de_opt_u32_from_opt_str(de).unwrap();
assert_eq!(result, expected);
}
How can I create a deserializer like
let de: StrDeserializer<ValueError> = string.into_deserializer();
but holding Option<String>
to satisfy
let s = Option::<String>::deserialize(deserializer)?;
Here is a deserializer for Option
that will work with any type that implements IntoDeserializer
(built to mimic the existing implementations in serde::de::value
):
use std::marker::PhantomData;
use serde::de::{Deserializer, Error, IntoDeserializer, Visitor}; // 1.0.160
struct OptionDeserializer<T, E> {
value: Option<T>,
marker: PhantomData<E>,
}
impl<T, E> OptionDeserializer<T, E> {
pub fn new(value: Option<T>) -> Self {
OptionDeserializer {
value,
marker: PhantomData,
}
}
}
impl<'de, T, E> Deserializer<'de> for OptionDeserializer<T, E>
where
T: IntoDeserializer<'de, E>,
E: Error,
{
type Error = E;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
match self.value {
Some(value) => visitor.visit_some(value.into_deserializer()),
None => visitor.visit_none(),
}
}
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
}
}
Using it, the missing line in your function would be:
let de: OptionDeserializer<&str, ValueError> = OptionDeserializer::new(maybe_string);
See a full demo with tests on the playground.