Created
February 13, 2023 10:38
-
-
Save tesaguri/3abb63bc7453393ddb696c44f7d5a206 to your computer and use it in GitHub Desktop.
An example `DeserializeSeed` implementation that transforms the underlying data structure
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use core::fmt; | |
use core::marker::PhantomData; | |
use serde::de::{self, IntoDeserializer}; | |
pub struct TransformMapSeed<T> { | |
seed: T, | |
} | |
pub type TransformMap<T> = TransformMapSeed<PhantomData<T>>; | |
impl<'de, T: de::Deserialize<'de>> TransformMap<T> { | |
pub fn new() -> Self { | |
Self { seed: PhantomData } | |
} | |
} | |
impl<'de, T: de::DeserializeSeed<'de>> TransformMapSeed<T> { | |
pub fn new_seed(seed: T) -> Self { | |
Self { seed } | |
} | |
} | |
impl<'de, T: de::DeserializeSeed<'de>> de::DeserializeSeed<'de> for TransformMapSeed<T> { | |
type Value = T::Value; | |
fn deserialize<D: de::Deserializer<'de>>(self, d: D) -> Result<T::Value, D::Error> { | |
struct Visitor<T>(T); | |
struct MapAccess<A>(A); | |
struct KeyProxySeed<T>(T); | |
return d.deserialize_map(Visitor(self.seed)); | |
impl<'de, T: de::DeserializeSeed<'de>> de::Visitor<'de> for Visitor<T> { | |
type Value = T::Value; | |
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
f.write_str("a map") | |
} | |
fn visit_map<A: de::MapAccess<'de>>(self, map: A) -> Result<T::Value, A::Error> { | |
self.0.deserialize(MapAccess(map)) | |
} | |
} | |
impl<'de, A: de::MapAccess<'de>> de::Deserializer<'de> for MapAccess<A> { | |
type Error = A::Error; | |
fn deserialize_any<V: de::Visitor<'de>>(self, v: V) -> Result<V::Value, A::Error> { | |
v.visit_map(self) | |
} | |
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 | |
} | |
} | |
impl<'de, A: de::MapAccess<'de>> de::MapAccess<'de> for MapAccess<A> { | |
type Error = A::Error; | |
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, A::Error> | |
where | |
T: de::DeserializeSeed<'de>, | |
{ | |
self.0.next_key_seed(KeyProxySeed(seed)) | |
} | |
fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value, A::Error> | |
where | |
T: de::DeserializeSeed<'de>, | |
{ | |
self.0.next_value_seed(seed) | |
} | |
fn next_entry_seed<T, U>( | |
&mut self, | |
kseed: T, | |
vseed: U, | |
) -> Result<Option<(T::Value, U::Value)>, A::Error> | |
where | |
T: de::DeserializeSeed<'de>, | |
U: de::DeserializeSeed<'de>, | |
{ | |
self.0.next_entry_seed(KeyProxySeed(kseed), vseed) | |
} | |
} | |
impl<'de, T: de::DeserializeSeed<'de>> de::DeserializeSeed<'de> for KeyProxySeed<T> { | |
type Value = T::Value; | |
fn deserialize<D: de::Deserializer<'de>>(self, d: D) -> Result<T::Value, D::Error> { | |
d.deserialize_identifier(self) | |
} | |
} | |
impl<'de, T: de::DeserializeSeed<'de>> de::Visitor<'de> for KeyProxySeed<T> { | |
type Value = T::Value; | |
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
f.write_str("an identifier") | |
} | |
fn visit_str<E: de::Error>(self, s: &str) -> Result<T::Value, E> { | |
let transformed = match s { | |
"foo" => "bar", | |
_ => s, | |
}; | |
self.0.deserialize(transformed.into_deserializer()) | |
} | |
} | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use serde::de::DeserializeSeed; | |
use super::*; | |
#[test] | |
fn it_works() { | |
#[derive(serde::Deserialize)] | |
struct Foo { | |
#[allow(dead_code)] | |
foo: u32, | |
} | |
#[derive(serde::Deserialize)] | |
struct Bar { | |
bar: u32, | |
} | |
let deserializer = | |
de::value::MapDeserializer::<_, de::value::Error>::new([("foo", 42)].into_iter()); | |
assert!(TransformMap::<Foo>::new() | |
.deserialize(deserializer.clone()) | |
.is_err()); | |
assert!(matches!( | |
TransformMap::new().deserialize(deserializer.clone()), | |
Ok(Bar { bar: 42 }) | |
)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment