rust_json_derive 0.1.2

ToJson and FromJson derive for rust-json
Documentation
use proc_macro2::Span;
use quote::quote;
use syn::{Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Ident, Index, Result, Variant};

use crate::attrs;

pub fn expand_serialize(input: DeriveInput) -> Result<proc_macro2::TokenStream> {
    let ident = &input.ident;
    match input.data {
        Data::Struct(struct_data) => Ok(serialize_struct(ident, struct_data)),
        Data::Enum(enum_data) => Ok(serialize_enum(ident, enum_data)),
        Data::Union(_) => Err(Error::new(
            ident.span(),
            "rust_json_derive not support union!",
        )),
    }
}

fn serialize_struct(ident: &Ident, struct_data: DataStruct) -> proc_macro2::TokenStream {
    let s = serialize_fields(ident, struct_data.fields);
    quote!(
        impl rust_json::ToJson for #ident {
            fn to_json(&self) -> rust_json::JsonElem {
                #s
            }
        }
    )
}

fn serialize_enum(ident: &Ident, enum_data: DataEnum) -> proc_macro2::TokenStream {
    let variants = enum_data.variants;
    let s = variants.iter().map(|v| serialize_varient(ident, v));
    quote!(
        impl rust_json::ToJson for #ident {
            fn to_json(&self) -> rust_json::JsonElem {
                match self {
                    #(#s),*
                }
            }
        }
    )
}

fn serialize_fields(ident: &Ident, fields: Fields) -> proc_macro2::TokenStream {
    match fields {
        Fields::Named(_) => {
            let s = fields
                .iter()
                .map(|f| {
                    (
                        attrs::get_name_from_attrs(&f.attrs, f.ident.as_ref().unwrap().to_string()),
                        f.ident.as_ref(),
                    )
                })
                .map(|(name, i)| quote!(hm.insert(#name.to_string(), self.#i.to_json());));
            quote!(
                {
                    let mut hm = std::collections::HashMap::<String, rust_json::JsonElem>::new();
                    #(#s)*
                    rust_json::JsonElem::Object(hm)
                }
            )
        }
        Fields::Unnamed(_) if fields.len() == 1 => {
            quote!(self.0.to_json())
        }
        Fields::Unnamed(_) => {
            let s = fields
                .iter()
                .enumerate()
                .map(|(i, _)| Index::from(i))
                .map(|i| quote!(vec.push(self.#i.to_json());));
            quote!(
                {
                    let mut vec = Vec::<rust_json::JsonElem>::new();
                    #(#s)*
                    rust_json::JsonElem::Array(vec)
                }
            )
        }
        Fields::Unit => {
            quote!({ rust_json::JsonElem::Str(stringify!(#ident).to_string()) })
        }
    }
}

fn serialize_varient(ident: &Ident, variant: &Variant) -> proc_macro2::TokenStream {
    let fields = &variant.fields;
    let var_ident = &variant.ident;
    let var_name = attrs::get_name_from_attrs(&variant.attrs, var_ident.to_string());
    match fields {
        Fields::Unit => {
            quote!(#ident::#var_ident => rust_json::JsonElem::Str(#var_name.to_string()))
        }
        Fields::Unnamed(_) if fields.len() == 1 => {
            quote!(#ident::#var_ident(__field) => {
                let mut hm = std::collections::HashMap::<String, rust_json::JsonElem>::new();
                hm.insert(#var_name.to_string(), __field.to_json());
                rust_json::JsonElem::Object(hm)
            })
        }
        Fields::Unnamed(_) => {
            let field_names =
                (0..fields.len()).map(|i| Ident::new(&format!("__field{}", i), Span::call_site()));
            let s = (0..fields.len())
                .map(|i| Ident::new(&format!("__field{}", i), Span::call_site()))
                .map(|i| quote!(vec.push(#i.to_json());));
            quote! {
                #ident::#var_ident(#(#field_names),*) => {
                    let mut vec = Vec::<rust_json::JsonElem>::new();
                    #(#s)*
                    let mut hm = std::collections::HashMap::<String, rust_json::JsonElem>::new();
                    hm.insert(#var_name.to_string(), rust_json::JsonElem::Array(vec));
                    rust_json::JsonElem::Object(hm)
                }
            }
        }
        Fields::Named(_) => {
            let field_names = fields.iter().map(|f| f.ident.as_ref());
            let s = fields
                .iter()
                .map(|f| {
                    (
                        attrs::get_name_from_attrs(&f.attrs, f.ident.as_ref().unwrap().to_string()),
                        f.ident.as_ref(),
                    )
                })
                .map(|(name, i)| quote!(hm.insert(#name.to_string(), #i.to_json());));
            quote! {
                #ident::#var_ident{#(#field_names),*} => {
                    let mut hm = std::collections::HashMap::<String, rust_json::JsonElem>::new();
                    #(#s)*
                    let mut hm_wrap = std::collections::HashMap::<String, rust_json::JsonElem>::new();
                    hm_wrap.insert(#var_name.to_string(), rust_json::JsonElem::Object(hm));
                    rust_json::JsonElem::Object(hm_wrap)
                }
            }
        }
    }
}