Skip to content

Instantly share code, notes, and snippets.

@Arnavion
Last active October 3, 2016 16:27
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 Arnavion/7841b1a9920a5daf0a6e9a23d4377cc6 to your computer and use it in GitHub Desktop.
Save Arnavion/7841b1a9920a5daf0a6e9a23d4377cc6 to your computer and use it in GitHub Desktop.
Macro for deserializing structs from JSON for Rust stable
//! Exports a single macro named make_deserializable that can be used in place of #[derive(serde::Deserialize)] on structs.
//! Supports regular structs and tuple structs of u64.
//! Only works with JSON deserialization.
//!
//! Eg: make_deserializable!(struct Foo { bar: String, baz: i32, });
//! Eg: make_deserializable!(struct Bar(u64));
macro_rules! impl_deserialize_struct {
(struct $struct_name:ident {
$($field_name:ident: $field_type:ty,)*
}) => {
impl serde::Deserialize for $struct_name {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: serde::Deserializer {
#[allow(non_camel_case_types)]
enum Field {
$($field_name,)*
Unknown,
}
impl serde::Deserialize for Field {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: serde::Deserializer {
struct FieldVisitor;
impl serde::de::Visitor for FieldVisitor {
type Value = Field;
fn visit_str<E>(&mut self, value: &str) -> Result<Field, E> where E: serde::Error {
match value {
$(stringify!($field_name) => Ok(Field::$field_name),)*
_ => Ok(Field::Unknown),
}
}
}
deserializer.deserialize_struct_field(FieldVisitor)
}
}
struct Visitor;
impl serde::de::Visitor for Visitor {
type Value = $struct_name;
fn visit_map<V>(&mut self, mut visitor: V) -> Result<Self::Value, V::Error> where V: serde::de::MapVisitor {
$(
let mut $field_name: Option<$field_type> = None;
)*
while let Some(key) = try!(visitor.visit_key::<Field>()) {
match key {
$(
Field::$field_name => {
if $field_name.is_some() {
return Err(<V::Error as serde::Error>::duplicate_field(stringify!($field_name)))
}
$field_name = Some(try!(visitor.visit_value()));
}
),*
Field::Unknown => {
try!(visitor.visit_value::<serde_json::Value>());
},
}
}
try!(visitor.end());
$(
let $field_name = match $field_name {
Some(x) => x,
None => try!(visitor.missing_field(stringify!($field_name)))
};
)*
Ok($struct_name {
$(
$field_name: $field_name,
)*
})
}
}
const FIELDS: &'static [&'static str] = &[$(stringify!($field_name),)*];
deserializer.deserialize_struct(stringify!($struct_name), FIELDS, Visitor)
}
}
}
}
macro_rules! impl_deserialize_u64 {
($struct_name:ident) => {
impl serde::Deserialize for $struct_name {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: serde::Deserializer {
struct Visitor;
impl serde::de::Visitor for Visitor {
type Value = $struct_name;
fn visit_u64<E>(&mut self, v: u64) -> Result<Self::Value, E> where E: serde::Error {
Ok($struct_name(v))
}
}
deserializer.deserialize_u64(Visitor)
}
}
};
}
#[macro_export]
macro_rules! make_deserializable {
(struct $struct_name:ident {
$($field_name:ident: $field_type:ty,)*
}) => {
#[derive(Debug)]
struct $struct_name {
$($field_name: $field_type),*
}
impl_deserialize_struct!(struct $struct_name {
$($field_name: $field_type,)*
});
};
(pub struct $struct_name:ident {
$($field_name:ident: $field_type:ty,)*
}) => {
#[derive(Debug)]
pub struct $struct_name {
$($field_name: $field_type),*
}
impl_deserialize_struct!(struct $struct_name {
$($field_name: $field_type,)*
});
};
(struct $struct_name:ident(u64)) => {
#[derive(Debug)]
struct $struct_name(u64);
impl_deserialize_u64!($struct_name);
};
(pub struct $struct_name:ident(u64)) => {
#[derive(Debug)]
pub struct $struct_name(u64);
impl_deserialize_u64!($struct_name);
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment