Skip to content

Instantly share code, notes, and snippets.

@conradludgate
Created November 5, 2023 22:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save conradludgate/088122b68163a9ac75d3e1fff9166863 to your computer and use it in GitHub Desktop.
Save conradludgate/088122b68163a9ac75d3e1fff9166863 to your computer and use it in GitHub Desktop.
eldritch horror rust serde json macro code
use serde::de::*;
use serde::forward_to_deserialize_any;
// -- macro code --
macro_rules! json {
( $($tt:tt)* ) => { json_internal!($($tt)*) };
}
macro_rules! json_internal {
({ $($tt:tt)+ }) => {{
#[derive(Copy, Clone)]
struct Map;
struct MapState(usize);
impl<'de> Deserializer<'de> for Map {
type Error = serde::de::value::Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_map(MapState(0))
}
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> MapAccess<'de> for MapState {
type Error = serde::de::value::Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
where
K: DeserializeSeed<'de>,
{
let n = self.0;
let res = json_internal!(@key (seed) n (0) () ($($tt)+));
self.0 += 1;
Ok(Some(res))
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
where
V: DeserializeSeed<'de>,
{
let n = self.0;
let res = json_internal!(@skipkey (seed) n (0) () ($($tt)+));
self.0 += 1;
Ok(res)
}
}
Map
}};
(@key ($f:expr) $n:ident ($i:expr) ($($key:tt)*) (: $($rest:tt)*)) => {
if $n == $i {
$f.deserialize(json_internal!($($key)*))?
} else {
json_internal!(@skipvalue ($f) $n ($i + 1) () ($($rest)*))
}
};
(@key ($f:expr) $n:ident ($i:expr) ($($key:tt)*) ($tt:tt $($rest:tt)*)) => {
json_internal!(@key ($f) $n ($i) ($($key)* $tt) ($($rest)*))
};
(@skipvalue ($f:expr) $n:ident ($i:expr) () (, $($rest:tt)+)) => {
json_internal!(@key ($f) $n ($i + 1) () ($($rest)*))
};
(@skipvalue ($f:expr) $n:ident ($i:expr) () ($(,)?)) => {
if $n == $i + 1 {
return Ok(None)
} else {
panic!("key")
}
};
(@skipvalue ($f:expr) $n:ident ($i:expr) () ($tt:tt $($rest:tt)*)) => {
json_internal!(@skipvalue ($f) $n ($i) () ($($rest)*))
};
(@value ($f:expr) $n:ident ($i:expr) ($($value:tt)*) (, $($rest:tt)*)) => {
if $n == $i {
$f.deserialize(json_internal!($($value)*))?
} else {
json_internal!(@skipkey ($f) $n ($i + 1) () ($($rest)*))
}
};
(@value ($f:expr) $n:ident ($i:expr) ($($value:tt)*) ($(,)?)) => {
if $n == $i {
$f.deserialize(json_internal!($($value)*))?
} else {
panic!("value")
}
};
(@value ($f:expr) $n:ident ($i:expr) ($($value:tt)*) ($tt:tt $($rest:tt)*)) => {
json_internal!(@value ($f) $n ($i) ($($value)* $tt) ($($rest)*))
};
(@skipkey ($f:expr) $n:ident ($i:expr) () (: $($rest:tt)+)) => {
json_internal!(@value ($f) $n ($i + 1) () ($($rest)*))
};
(@skipkey ($f:expr) $n:ident ($i:expr) () ($tt:tt $($rest:tt)*)) => {
json_internal!(@skipkey ($f) $n ($i) () ($($rest)*))
};
($x:literal) => { Lit($x) };
}
// -- library code --
struct Lit<T>(T);
impl<'de> Deserializer<'de> for Lit<i64> {
type Error = serde::de::value::Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_i64(self.0)
}
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> Deserializer<'de> for Lit<&'static str> {
type Error = serde::de::value::Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_borrowed_str(self.0)
}
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
}
}
// -- user code --
use std::collections::BTreeMap;
#[derive(Debug, serde::Deserialize)]
struct Something {
foo: i32,
}
fn main() {
let data = json!({"foo": 123});
let x = Something::deserialize(data).unwrap();
let y = <BTreeMap<&'static str, i32>>::deserialize(data).unwrap();
assert_eq!(x.foo, 123);
assert_eq!(y["foo"], 123);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment