Skip to content

Commit

Permalink
scroll_derive: add generics support to derive
Browse files Browse the repository at this point in the history
  • Loading branch information
Systemcluster authored and m4b committed May 8, 2020
1 parent c9b950d commit ea70080
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 26 deletions.
197 changes: 171 additions & 26 deletions scroll_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use quote::quote;

use proc_macro::TokenStream;

fn impl_struct(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>) -> proc_macro2::TokenStream {
fn impl_struct(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, generics: &syn::Generics) -> proc_macro2::TokenStream {
let items: Vec<_> = fields.iter().enumerate().map(|(i, f)| {
let ident = &f.ident.as_ref().map(|i|quote!{#i}).unwrap_or({let t = proc_macro2::Literal::usize_unsuffixed(i); quote!{#t}});
let ty = &f.ty;
Expand All @@ -16,7 +16,7 @@ fn impl_struct(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Fiel
syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(ref int), ..}) => {
let size = int.base10_parse::<usize>().unwrap();
quote! {
#ident: { let mut __tmp: #ty = [0; #size]; src.gread_inout_with(offset, &mut __tmp, ctx)?; __tmp }
#ident: { let mut __tmp: #ty = [0u8.into(); #size]; src.gread_inout_with(offset, &mut __tmp, ctx)?; __tmp }
}
},
_ => panic!("Pread derive with bad array constexpr")
Expand All @@ -30,14 +30,40 @@ fn impl_struct(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Fiel
}
}).collect();

let gl = &generics.lt_token;
let gp = &generics.params;
let gg = &generics.gt_token;
let gn = gp.iter().map(|param: &syn::GenericParam| match param {
syn::GenericParam::Type(ref t) => {
let ident = &t.ident;
quote! { #ident }
},
p => quote! { #p }
});
let gn = quote! { #gl #( #gn ),* #gg };
let gw = if !gp.is_empty() {
let gi = gp.iter().map(|param: &syn::GenericParam| match param {
syn::GenericParam::Type(ref t) => {
let ident = &t.ident;
quote! {
#ident : ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian> + ::std::convert::From<u8> + ::std::marker::Copy,
::scroll::Error : ::std::convert::From<< #ident as ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian>>::Error>,
< #ident as ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian>>::Error : ::std::convert::From<scroll::Error>
}
},
p => quote! { #p }
});
quote! { #( #gi ),* , }
} else { quote! { } };

quote! {
impl<'a> ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian> for #name where #name: 'a {
impl<'a, #gp > ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian> for #name #gn where #gw #name #gn : 'a {
type Error = ::scroll::Error;
#[inline]
fn try_from_ctx(src: &'a [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<(Self, usize), Self::Error> {
use ::scroll::Pread;
let offset = &mut 0;
let data = #name { #(#items,)* };
let data = Self { #(#items,)* };
Ok((data, *offset))
}
}
Expand All @@ -46,14 +72,15 @@ fn impl_struct(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Fiel

fn impl_try_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
let name = &ast.ident;
let generics = &ast.generics;
match ast.data {
syn::Data::Struct(ref data) => {
match data.fields {
syn::Fields::Named(ref fields) => {
impl_struct(name, &fields.named)
impl_struct(name, &fields.named, generics)
},
syn::Fields::Unnamed(ref fields) => {
impl_struct(name, &fields.unnamed)
impl_struct(name, &fields.unnamed, generics)
},
_ => {
panic!("Pread can not be derived for unit structs")
Expand All @@ -71,7 +98,7 @@ pub fn derive_pread(input: TokenStream) -> TokenStream {
gen.into()
}

fn impl_try_into_ctx(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>) -> proc_macro2::TokenStream {
fn impl_try_into_ctx(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, generics: &syn::Generics) -> proc_macro2::TokenStream {
let items: Vec<_> = fields.iter().enumerate().map(|(i, f)| {
let ident = &f.ident.as_ref().map(|i|quote!{#i}).unwrap_or({let t = proc_macro2::Literal::usize_unsuffixed(i); quote!{#t}});
let ty = &f.ty;
Expand All @@ -91,8 +118,49 @@ fn impl_try_into_ctx(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn
}
}).collect();

let gl = &generics.lt_token;
let gp = &generics.params;
let gg = &generics.gt_token;
let gn = gp.iter().map(|param: &syn::GenericParam| match param {
syn::GenericParam::Type(ref t) => {
let ident = &t.ident;
quote! { #ident }
},
p => quote! { #p }
});
let gn = quote! { #gl #( #gn ),* #gg };
let gwref = if !gp.is_empty() {
let gi = gp.iter().map(|param: &syn::GenericParam| match param {
syn::GenericParam::Type(ref t) => {
let ident = &t.ident;
quote! {
&'a #ident : ::scroll::ctx::TryIntoCtx<::scroll::Endian>,
::scroll::Error: ::std::convert::From<<&'a #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error>,
<&'a #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error: ::std::convert::From<scroll::Error>
}
},
p => quote! { #p }
});
quote! { where #( #gi ),* }
} else { quote! { } };
let gw = if !gp.is_empty() {
let gi = gp.iter().map(|param: &syn::GenericParam| match param {
syn::GenericParam::Type(ref t) => {
let ident = &t.ident;
quote! {
#ident : ::scroll::ctx::TryIntoCtx<::scroll::Endian>,
::scroll::Error: ::std::convert::From<<#ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error>,
<#ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error: ::std::convert::From<scroll::Error>
}
},
p => quote! { #p }
});
quote! { where Self: ::std::marker::Copy, #( #gi ),* }
} else { quote! { } };


quote! {
impl<'a> ::scroll::ctx::TryIntoCtx<::scroll::Endian> for &'a #name {
impl<'a, #gp > ::scroll::ctx::TryIntoCtx<::scroll::Endian> for &'a #name #gn #gwref {
type Error = ::scroll::Error;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<usize, Self::Error> {
Expand All @@ -103,7 +171,7 @@ fn impl_try_into_ctx(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn
}
}

impl ::scroll::ctx::TryIntoCtx<::scroll::Endian> for #name {
impl #gl #gp #gg ::scroll::ctx::TryIntoCtx<::scroll::Endian> for #name #gn #gw {
type Error = ::scroll::Error;
#[inline]
fn try_into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<usize, Self::Error> {
Expand All @@ -115,14 +183,15 @@ fn impl_try_into_ctx(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn

fn impl_pwrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
let name = &ast.ident;
let generics = &ast.generics;
match ast.data {
syn::Data::Struct(ref data) => {
match data.fields {
syn::Fields::Named(ref fields) => {
impl_try_into_ctx(name, &fields.named)
impl_try_into_ctx(name, &fields.named, generics)
},
syn::Fields::Unnamed(ref fields) => {
impl_try_into_ctx(name, &fields.unnamed)
impl_try_into_ctx(name, &fields.unnamed, generics)
},
_ => {
panic!("Pwrite can not be derived for unit structs")
Expand All @@ -140,7 +209,7 @@ pub fn derive_pwrite(input: TokenStream) -> TokenStream {
gen.into()
}

fn size_with(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>) -> proc_macro2::TokenStream {
fn size_with(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, generics: &syn::Generics) -> proc_macro2::TokenStream {
let items: Vec<_> = fields.iter().map(|f| {
let ty = &f.ty;
match *ty {
Expand All @@ -163,8 +232,33 @@ fn size_with(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field,
}
}
}).collect();

let gl = &generics.lt_token;
let gp = &generics.params;
let gg = &generics.gt_token;
let gn = gp.iter().map(|param: &syn::GenericParam| match param {
syn::GenericParam::Type(ref t) => {
let ident = &t.ident;
quote! { #ident }
},
p => quote! { #p }
});
let gn = quote! { #gl #( #gn ),* #gg };
let gw = if !gp.is_empty() {
let gi = gp.iter().map(|param: &syn::GenericParam| match param {
syn::GenericParam::Type(ref t) => {
let ident = &t.ident;
quote! {
#ident : ::scroll::ctx::SizeWith<::scroll::Endian>
}
},
p => quote! { #p }
});
quote! { where #( #gi ),* }
} else { quote! { } };

quote! {
impl ::scroll::ctx::SizeWith<::scroll::Endian> for #name {
impl #gl #gp #gg ::scroll::ctx::SizeWith<::scroll::Endian> for #name #gn #gw {
#[inline]
fn size_with(ctx: &::scroll::Endian) -> usize {
0 #(+ #items)*
Expand All @@ -175,14 +269,15 @@ fn size_with(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field,

fn impl_size_with(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
let name = &ast.ident;
let generics = &ast.generics;
match ast.data {
syn::Data::Struct(ref data) => {
match data.fields {
syn::Fields::Named(ref fields) => {
size_with(name, &fields.named)
size_with(name, &fields.named, generics)
},
syn::Fields::Unnamed(ref fields) => {
size_with(name, &fields.unnamed)
size_with(name, &fields.unnamed, generics)
},
_ => {

Expand All @@ -201,7 +296,7 @@ pub fn derive_sizewith(input: TokenStream) -> TokenStream {
gen.into()
}

fn impl_cread_struct(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>) -> proc_macro2::TokenStream {
fn impl_cread_struct(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, generics: &syn::Generics) -> proc_macro2::TokenStream {
let items: Vec<_> = fields.iter().enumerate().map(|(i, f)| {
let ident = &f.ident.as_ref().map(|i|quote!{#i}).unwrap_or({let t = proc_macro2::Literal::usize_unsuffixed(i); quote!{#t}});
let ty = &f.ty;
Expand All @@ -214,7 +309,7 @@ fn impl_cread_struct(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn
let incr = quote! { ::scroll::export::mem::size_of::<#arrty>() };
quote! {
#ident: {
let mut __tmp: #ty = [0; #size];
let mut __tmp: #ty = [0u8.into(); #size];
for i in 0..__tmp.len() {
__tmp[i] = src.cread_with(*offset, ctx);
*offset += #incr;
Expand All @@ -235,13 +330,37 @@ fn impl_cread_struct(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn
}
}).collect();

let gl = &generics.lt_token;
let gp = &generics.params;
let gg = &generics.gt_token;
let gn = gp.iter().map(|param: &syn::GenericParam| match param {
syn::GenericParam::Type(ref t) => {
let ident = &t.ident;
quote! { #ident }
},
p => quote! { #p }
});
let gn = quote! { #gl #( #gn ),* #gg };
let gw = if !gp.is_empty() {
let gi = gp.iter().map(|param: &syn::GenericParam| match param {
syn::GenericParam::Type(ref t) => {
let ident = &t.ident;
quote! {
#ident : ::scroll::ctx::FromCtx<::scroll::Endian> + ::std::convert::From<u8> + ::std::marker::Copy
}
},
p => quote! { #p }
});
quote! { where #( #gi ),* , }
} else { quote! { } };

quote! {
impl ::scroll::ctx::FromCtx<::scroll::Endian> for #name {
impl #gl #gp #gg ::scroll::ctx::FromCtx<::scroll::Endian> for #name #gn #gw {
#[inline]
fn from_ctx(src: &[u8], ctx: ::scroll::Endian) -> Self {
use ::scroll::Cread;
let offset = &mut 0;
let data = #name { #(#items,)* };
let data = Self { #(#items,)* };
data
}
}
Expand All @@ -250,14 +369,15 @@ fn impl_cread_struct(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn

fn impl_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
let name = &ast.ident;
let generics = &ast.generics;
match ast.data {
syn::Data::Struct(ref data) => {
match data.fields {
syn::Fields::Named(ref fields) => {
impl_cread_struct(name, &fields.named)
impl_cread_struct(name, &fields.named, generics)
},
syn::Fields::Unnamed(ref fields) => {
impl_cread_struct(name, &fields.unnamed)
impl_cread_struct(name, &fields.unnamed, generics)
},
_ => {
panic!("IOread can not be derived for unit structs")
Expand All @@ -275,7 +395,7 @@ pub fn derive_ioread(input: TokenStream) -> TokenStream {
gen.into()
}

fn impl_into_ctx(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>) -> proc_macro2::TokenStream {
fn impl_into_ctx(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, generics: &syn::Generics) -> proc_macro2::TokenStream {
let items: Vec<_> = fields.iter().enumerate().map(|(i, f)| {
let ident = &f.ident.as_ref().map(|i|quote!{#i}).unwrap_or({let t = proc_macro2::Literal::usize_unsuffixed(i); quote!{#t}});
let ty = &f.ty;
Expand All @@ -300,8 +420,32 @@ fn impl_into_ctx(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Fi
}
}).collect();

let gl = &generics.lt_token;
let gp = &generics.params;
let gg = &generics.gt_token;
let gn = gp.iter().map(|param: &syn::GenericParam| match param {
syn::GenericParam::Type(ref t) => {
let ident = &t.ident;
quote! { #ident }
},
p => quote! { #p }
});
let gw = if !gp.is_empty() {
let gi = gp.iter().map(|param: &syn::GenericParam| match param {
syn::GenericParam::Type(ref t) => {
let ident = &t.ident;
quote! {
#ident : ::scroll::ctx::IntoCtx<::scroll::Endian> + ::std::marker::Copy
}
},
p => quote! { #p }
});
quote! { where #( #gi ),* }
} else { quote! { } };
let gn = quote! { #gl #( #gn ),* #gg };

quote! {
impl<'a> ::scroll::ctx::IntoCtx<::scroll::Endian> for &'a #name {
impl<'a, #gp > ::scroll::ctx::IntoCtx<::scroll::Endian> for &'a #name #gn #gw {
#[inline]
fn into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) {
use ::scroll::Cwrite;
Expand All @@ -311,7 +455,7 @@ fn impl_into_ctx(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Fi
}
}

impl ::scroll::ctx::IntoCtx<::scroll::Endian> for #name {
impl #gl #gp #gg ::scroll::ctx::IntoCtx<::scroll::Endian> for #name #gn #gw {
#[inline]
fn into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) {
(&self).into_ctx(dst, ctx)
Expand All @@ -322,14 +466,15 @@ fn impl_into_ctx(name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Fi

fn impl_iowrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
let name = &ast.ident;
let generics = &ast.generics;
match ast.data {
syn::Data::Struct(ref data) => {
match data.fields {
syn::Fields::Named(ref fields) => {
impl_into_ctx(name, &fields.named)
impl_into_ctx(name, &fields.named, generics)
},
syn::Fields::Unnamed(ref fields) => {
impl_into_ctx(name, &fields.unnamed)
impl_into_ctx(name, &fields.unnamed, generics)
},
_ => {
panic!("IOwrite can not be derived for unit structs")
Expand Down
Loading

0 comments on commit ea70080

Please sign in to comment.