use proc_macro2::TokenStream; use quote::quote; use syn::{Data, DeriveInput, Fields, Ident, parse_macro_input}; pub fn impl_decodeable(item: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(item as DeriveInput); let input_name = &input.ident; match &input.data { Data::Struct(st) => { let decode_calls = impl_decode_calls_for_fields(&st.fields); quote! { impl ::trigger_encoding::Decodeable for #input_name { fn decode(r: &mut R) -> ::std::io::Result { Ok(Self { #decode_calls }) } } } .into() } Data::Enum(enumeration) => { let repr = input .attrs .iter() .find(|attr| attr.path().is_ident("repr")) .expect("#[derive(Decodeable)] requires #[repr(ty)] attribute for enums") .parse_args::() .unwrap(); let mut decode_match_arms = TokenStream::new(); for variant in enumeration.variants.iter() { let variant_name = &variant.ident; let (_, discriminant) = variant.discriminant.as_ref().expect( "#[derive(Decodeable)] requires explicit enum discriminant declaration", ); let decode_calls = impl_decode_calls_for_fields(&variant.fields); decode_match_arms.extend(quote! { #discriminant => Self::#variant_name { #decode_calls }, }); } quote! { impl ::trigger_encoding::Decodeable for #input_name { fn decode(r: &mut R) -> ::std::io::Result { Ok(match #repr::decode(r)? { #decode_match_arms unk => panic!("{}::decode: unknown discriminant encountered: {}", stringify!(#input_name), unk), }) } } }.into() } Data::Union(_) => panic!("#[derive(Decodeable)] doesn't support unions"), } } fn impl_decode_calls_for_fields(fields: &Fields) -> TokenStream { let mut decode_calls = TokenStream::new(); fields.iter().for_each(|field| { let name = field.ident.as_ref().unwrap(); decode_calls.extend(quote! { #name: ::trigger_encoding::Decodeable::decode(r)?, }); }); decode_calls }