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 { 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::::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::::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::::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::::new(); #(#s)* let mut hm = std::collections::HashMap::::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::::new(); #(#s)* let mut hm_wrap = std::collections::HashMap::::new(); hm_wrap.insert(#var_name.to_string(), rust_json::JsonElem::Object(hm)); rust_json::JsonElem::Object(hm_wrap) } } } } }