-rw-r--r--src/lib.rs210
-rw-r--r--tests/x.rs15
2 files changed, 141 insertions, 84 deletions
diff --git a/src/lib.rs b/src/lib.rs
index a6e76e2..7e77a4e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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()
}
diff --git a/tests/x.rs b/tests/x.rs
index 1a53eee..d24ae8e 100644
--- a/tests/x.rs
+++ b/tests/x.rs
@@ -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,
+}