Skip to content

Instantly share code, notes, and snippets.

@tesaguri
Created February 13, 2023 10:38
Show Gist options
  • Save tesaguri/3abb63bc7453393ddb696c44f7d5a206 to your computer and use it in GitHub Desktop.
Save tesaguri/3abb63bc7453393ddb696c44f7d5a206 to your computer and use it in GitHub Desktop.
An example `DeserializeSeed` implementation that transforms the underlying data structure
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