use proc_macro::{self, TokenStream};
use quote::quote;
use syn::{Data, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, Ident};
#[proc_macro_derive(Serialize)]
pub fn serialize_derive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
match impl_serialize(ast) {
Ok(ts) => ts,
Err(e) => compile_error(&e),
}
}
fn impl_serialize(value: DeriveInput) -> Result<TokenStream, String> {
let name = value.ident;
let r#impl = match value.data {
Data::Struct(s) => match s.fields {
Fields::Named(fields) => impl_serialize_for_struct_with_named_fields(name, fields),
Fields::Unnamed(fields) => impl_serialize_for_struct_with_unnamed_fields(name, fields),
Fields::Unit => impl_serialize_for_unit_struct(name),
},
_ => return Err("Expected struct".to_string()),
};
Ok(r#impl)
}
fn impl_serialize_for_struct_with_named_fields(name: Ident, fields: FieldsNamed) -> TokenStream {
let mut hashmap = Vec::new();
hashmap.push(quote! {
let mut fields = std::collections::hash_map::HashMap::new();
});
hashmap.append(
&mut fields
.named
.into_iter()
.map(|field| {
let field = field.ident.unwrap();
quote! {
fields.insert(stringify!(#field).to_string(), self.#field.serialize());
}
})
.collect::<Vec<_>>(),
);
let gen = quote! {
impl my_serde_json_library::Serialize for #name {
fn serialize(&self) -> my_serde_json_library::Value {
#(#hashmap)*
my_serde_json_library::Value::Object(fields)
}
}
};
gen.into()
}
fn impl_serialize_for_struct_with_unnamed_fields(
name: Ident,
fields: FieldsUnnamed,
) -> TokenStream {
let mut hashmap = Vec::new();
hashmap.push(quote! {
let mut fields = std::collections::hash_map::HashMap::new();
});
hashmap.append(
&mut fields
.unnamed
.into_iter()
.enumerate()
.map(|(i, _)| {
let index = syn::Index::from(i);
quote! {
fields.insert(stringify!(#index).to_string(), self.#index.serialize());
}
})
.collect::<Vec<_>>(),
);
let gen = quote! {
impl my_serde_json_library::Serialize for #name {
fn serialize(&self) -> my_serde_json_library::Value {
#(#hashmap)*
my_serde_json_library::Value::Object(fields)
}
}
};
gen.into()
}
fn impl_serialize_for_unit_struct(name: Ident) -> TokenStream {
let hashmap = quote! {
let mut fields = std::collections::hash_map::HashMap::new();
};
let gen = quote! {
impl my_serde_json_library::Serialize for #name {
fn serialize(&self) -> my_serde_json_library::Value {
#hashmap
my_serde_json_library::Value::Object(fields)
}
}
};
gen.into()
}
fn compile_error(error: &str) -> TokenStream {
format!("compile_error!(\"{error}\");").parse().unwrap()
}