| -rw-r--r-- | src/lib.rs | 210 | ||||
| -rw-r--r-- | tests/x.rs | 15 |
2 files changed, 141 insertions, 84 deletions
@@ -1,93 +1,145 @@ +use parse::Parse; use proc_macro::TokenStream; use quote::quote; use spanned::Spanned; use syn::*; -#[proc_macro_derive(Write, attributes(raad))] -/// Types are written in order of declaration. -pub fn impl_write(input: TokenStream) -> TokenStream { - let DeriveInput { - data, - ident, - vis, - generics, - .. - } = parse_macro_input!(input as DeriveInput); - let Data::Struct(DataStruct { fields, .. }) = data else { - return syn::Error::new(vis.span(), "only structs are supported for codegen") - .to_compile_error() - .into(); - }; - let fields_le = fields - .iter() - .zip((0..).map(proc_macro2::Literal::usize_unsuffixed)) - .map(|(Field { ident, .. }, i)| match ident { - None => quote! { ::raad::le::W::w(to, self.#i)?; }, - Some(i) => quote! { ::raad::le::W::w(to, self.#i)?; }, - }); - let fields_be = fields - .iter() - .zip((0..).map(proc_macro2::Literal::usize_unsuffixed)) - .map(|(Field { ident, .. }, i)| match ident { - None => quote! { ::raad::be::W::w(to, self.#i)?; }, - Some(i) => quote! { ::raad::be::W::w(to, self.#i)?; }, - }); - quote! { - impl ::raad::le::Writable for #ident { - fn _w(self, to: &mut impl ::std::io::Write) -> ::std::io::Result<()> { - #(#fields_le)* - Ok(()) - } + +#[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)?; }, } - impl ::raad::be::Writable for #ident { - fn _w(self, to: &mut impl ::std::io::Write) -> ::std::io::Result<()> { - #(#fields_be)* - Ok(()) + } + 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)?, } } } } - .into() +} + +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<Ident>, Generics), + ident: Ident, + fields: Vec<PField>, +} + +impl Parse for Input { + fn parse(input: parse::ParseStream) -> Result<Self> { + let DeriveInput { + data, + ident, + generics, + attrs, + .. + } = input.parse::<DeriveInput>()?; + 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 DeriveInput { - data, - ident, - vis, - generics, - .. - } = parse_macro_input!(input as DeriveInput); - let Data::Struct(DataStruct { fields, .. }) = data else { - return syn::Error::new(vis.span(), "only structs are supported for codegen") - .to_compile_error() - .into(); - }; - let fields_le = fields - .iter() - .zip((0..).map(proc_macro2::Literal::usize_unsuffixed)) - .map(|(Field { ident, .. }, i)| match ident { - None => quote! { #i: ::raad::le::R::r(from)?, }, - Some(i) => quote! { #i: ::raad::le::R::r(from)?, }, - }); - let fields_be = fields - .iter() - .zip((0..).map(proc_macro2::Literal::usize_unsuffixed)) - .map(|(Field { ident, .. }, i)| match ident { - None => quote! { #i: ::raad::be::R::r(from)?, }, - Some(i) => quote! { #i: ::raad::be::R::r(from)?, }, - }); - quote! { - impl ::raad::le::Readable for #ident { - fn r(from: &mut impl std::io::Read) -> ::std::io::Result<Self> { - Ok(Self { #(#fields_le)* }) - } - } - impl ::raad::be::Readable for #ident { - fn r(from: &mut impl ::std::io::Read) -> ::std::io::Result<Self> { - Ok(Self { #(#fields_be)* }) - } - } - } - .into() + 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<Self> { + 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<Self> { + Ok(Self { #(#fields_be)* }) + } + }, + ); + quote! { #le_block #be_block }.into() } @@ -1,6 +1,11 @@ -#[derive(raad_codegen::Read, raad_codegen::Write)] -struct X { - y: u8, -} #[derive(raad_codegen::Write, raad_codegen::Read)] -struct Z(X); +struct Header<T, U> { + #[raad(equals)] + magic: [u8; 4], + width: u32, + height: u32, + channels: u8, + colorspace: u8, + yar: T, + var: U, +} |