1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
use proc_macro::TokenStream; use quote::{quote, quote_spanned}; use syn::spanned::Spanned; use syn::{Data, Fields, Ident}; #[proc_macro_derive(Codegen)] pub fn derive_codegen(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); impl_codegen(&ast) } fn fields_codegen_match(fields: &Fields) -> proc_macro2::TokenStream { match fields { Fields::Named(ref fields) => { let f = fields.named.iter().map(|f| { let name = &f.ident; quote_spanned! {f.span()=>#name} }); quote! {{#(#f, )*}} } Fields::Unnamed(ref fields) => { let f = fields.unnamed.iter().enumerate().map(|(i, f)| { let name = Ident::new(&format!("f{}", i), syn::export::Span::call_site()); quote_spanned! {f.span()=>#name} }); quote! {(#(#f, )*)} } Fields::Unit => { quote! {} } } } fn fields_codegen_body(fields: &Fields) -> proc_macro2::TokenStream { match fields { Fields::Named(ref fields) => { let f = fields.named.iter().map(|f| { let name = &f.ident; quote_spanned! {f.span()=>{ out.push_str(stringify!(#name)); out.push(':'); out.push_str(&res.add_value(&#name)); }} }); quote! { out.push('{'); #(#f; out.push(','); )* out.push('}'); } } Fields::Unnamed(ref fields) => { let f = fields.unnamed.iter().enumerate().map(|(i, f)| { let name = Ident::new(&format!("f{}", i), syn::export::Span::call_site()); quote_spanned! {f.span()=>{ out.push_str(&res.add_value(&#name)); }} }); quote! { out.push('('); #(#f; out.push(','); )* out.push(')'); } } Fields::Unit => { quote! {} } } } fn impl_codegen(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; let out = match &ast.data { Data::Struct(ref data) => { let head = fields_codegen_match(&data.fields); let body = fields_codegen_body(&data.fields); quote! { let #name #head = &self; out.push_str(stringify!(#name)); #body } } Data::Enum(data) => { let variants = data.variants.iter().map(|v| { let var_name = &v.ident; let match_q = fields_codegen_match(&v.fields); let match_b = fields_codegen_body(&v.fields); quote_spanned! {v.span()=> #name::#var_name #match_q => { out.push_str(stringify!(#var_name)); #match_b } } }); quote! { out.push_str(stringify!(#name)); out.push_str("::"); match self { #(#variants ,)* } } } Data::Union(_) => unimplemented!(), }; let gen = quote! { impl ::structdump::Codegen for #name { fn gen_code(&self, res: &mut ::structdump::CodegenResult, out: &mut ::std::string::String) { #out } } }; gen.into() }