use proc_macro::TokenStream; use proc_macro2 as pm2; use quote::ToTokens; use syn::spanned::Spanned; use crate::common::{add_trait_bounds, destructure, pos_field_name}; pub fn derive(input: TokenStream) -> TokenStream { // Parse the input tokens into a syntax tree let input = parse_macro_input!(input as syn::DeriveInput); let e_generics = add_trait_bounds(input.generics, parse_quote!(orchid_api_traits::Encode)); let (e_impl_generics, e_ty_generics, e_where_clause) = e_generics.split_for_impl(); let name = input.ident; let encode = encode_body(&input.data); let expanded = quote! { impl #e_impl_generics orchid_api_traits::Encode for #name #e_ty_generics #e_where_clause { async fn encode( &self, mut write: std::pin::Pin<&mut W> ) { #encode } } }; TokenStream::from(expanded) } fn encode_body(data: &syn::Data) -> Option { match data { syn::Data::Union(_) => panic!("Unions can't be deserialized"), syn::Data::Struct(str) => { let dest = destructure(&str.fields)?; let body = encode_items(&str.fields); Some(quote! { let Self #dest = &self; #body }) }, syn::Data::Enum(en) => { let options = en.variants.iter().enumerate().map(|(i, v @ syn::Variant { ident, .. })| { let dest = destructure(&v.fields).unwrap_or_default(); let body = encode_items(&v.fields); quote! { Self::#ident #dest => { (Box::pin((#i as u8).encode(write.as_mut())) as std::pin::Pin>>).await; #body } } }); Some(quote! { match self { #(#options)* _ => unreachable!("Autogenerated encode impl for all possible variants"), } }) }, } } fn encode_names(names: impl Iterator) -> pm2::TokenStream { quote! { #( (Box::pin(#names .encode(write.as_mut())) as std::pin::Pin>>).await; )* } } fn encode_items(fields: &syn::Fields) -> Option { match fields { syn::Fields::Unit => None, syn::Fields::Named(_) => Some(encode_names(fields.iter().map(|f| f.ident.as_ref().unwrap()))), syn::Fields::Unnamed(un) => Some(encode_names((0..fields.len()).map(|i| pos_field_name(i, un.span())))), } }