use parse::Parse; use proc_macro::TokenStream; use quote::quote; use spanned::Spanned; use syn::*; #[derive(Clone)] enum PField { Basic(Ident, Type), Tuple(u16, Type), } impl PField { fn write(&self, module: proc_macro2::TokenStream) -> proc_macro2::TokenStream { match self { PField::Basic(i, _) => quote! { ::raad::#module::W::w(to, self.#i)?; }, PField::Tuple(i, _) => quote! { ::raad::#module::W::w(to, self.#i)?; }, } } fn read(&self, module: proc_macro2::TokenStream) -> proc_macro2::TokenStream { match self { PField::Basic(i, _) => quote! { #i: ::raad::#module::R::r(from)?, }, PField::Tuple(i, _) => { let i = proc_macro2::Literal::u16_unsuffixed(*i); quote! { #i: ::raad::#module::R::r(from)?, } } } } } impl Input { fn impl_block( &self, trt: proc_macro2::TokenStream, body: proc_macro2::TokenStream, ) -> proc_macro2::TokenStream { let Self { generics: (params, generics), ident, .. } = self; let (intro_generics, fwd_generics, maybe_where_clause) = generics.split_for_impl(); let binding = if maybe_where_clause.is_none() { quote!(where #(#params: ::raad::#trt,)*) } else { quote!(,#(#params: ::raad::#trt,)*) }; quote! { impl #intro_generics ::raad::#trt for #ident #fwd_generics #maybe_where_clause #binding { #body } } } } struct Input { generics: (Vec, Generics), ident: Ident, fields: Vec, } impl Parse for Input { fn parse(input: parse::ParseStream) -> Result { let DeriveInput { data, ident, generics, attrs, .. } = input.parse::()?; let params = generics.params.iter().filter_map(|x| match x { GenericParam::Type(x) => Some(x.ident.clone()), _ => None, }); let Data::Struct(DataStruct { fields, .. }) = data else { return Err(input.error("only structs are supported for codegen")); }; let fields = fields .iter() .zip(0..) .map(|(Field { ident, ty, .. }, i)| match ident { None => PField::Tuple(i, ty.clone()), Some(i) => PField::Basic(i.clone(), ty.clone()), }) .collect(); Ok(Input { fields, ident, generics: (params.collect(), generics), }) } } #[proc_macro_derive(Write, attributes(raad))] /// Types are written in order of declaration. pub fn impl_write(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as Input); let fields_le = input.fields.iter().map(|x| x.write(quote!(le))); let fields_be = input.fields.iter().map(|x| x.write(quote!(be))); let le_block = input.impl_block( quote!(le::Writable), quote! { fn _w(self, to: &mut impl ::std::io::Write) -> ::std::io::Result<()> { #(#fields_le)* Ok(()) } }, ); let be_block = input.impl_block( quote!(be::Writable), quote! { fn _w(self, to: &mut impl ::std::io::Write) -> ::std::io::Result<()> { #(#fields_be)* Ok(()) } }, ); quote! { #le_block #be_block }.into() } #[proc_macro_derive(Read, attributes(raad))] /// Types are read in order of declaration. pub fn impl_read(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as Input); let fields_le = input.fields.iter().map(|x| x.read(quote!(le))); let fields_be = input.fields.iter().map(|x| x.read(quote!(be))); let le_block = input.impl_block( quote!(le::Readable), quote! { fn r(from: &mut impl std::io::Read) -> ::std::io::Result { Ok(Self { #(#fields_le)* }) } }, ); let be_block = input.impl_block( quote!(be::Readable), quote! { fn r(from: &mut impl std::io::Read) -> ::std::io::Result { Ok(Self { #(#fields_be)* }) } }, ); quote! { #le_block #be_block }.into() }