Created
November 5, 2023 22:04
-
-
Save conradludgate/088122b68163a9ac75d3e1fff9166863 to your computer and use it in GitHub Desktop.
eldritch horror rust serde json macro code
This file contains hidden or 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 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