1 #![recursion_limit = "1024"]
2 
3 extern crate proc_macro;
4 use proc_macro2;
5 use quote::quote;
6 
7 use proc_macro::TokenStream;
8 
impl_field(ident: &proc_macro2::TokenStream, ty: &syn::Type) -> proc_macro2::TokenStream9 fn impl_field(ident: &proc_macro2::TokenStream, ty: &syn::Type) -> proc_macro2::TokenStream {
10     match *ty {
11         syn::Type::Array(ref array) => match array.len {
12             syn::Expr::Lit(syn::ExprLit {
13                 lit: syn::Lit::Int(ref int),
14                 ..
15             }) => {
16                 let size = int.base10_parse::<usize>().unwrap();
17                 quote! {
18                     #ident: { let mut __tmp: #ty = [0u8.into(); #size]; src.gread_inout_with(offset, &mut __tmp, ctx)?; __tmp }
19                 }
20             }
21             _ => panic!("Pread derive with bad array constexpr"),
22         },
23         syn::Type::Group(ref group) => impl_field(ident, &group.elem),
24         _ => {
25             quote! {
26                 #ident: src.gread_with::<#ty>(offset, ctx)?
27             }
28         }
29     }
30 }
31 
impl_struct( name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, generics: &syn::Generics, ) -> proc_macro2::TokenStream32 fn impl_struct(
33     name: &syn::Ident,
34     fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
35     generics: &syn::Generics,
36 ) -> proc_macro2::TokenStream {
37     let items: Vec<_> = fields
38         .iter()
39         .enumerate()
40         .map(|(i, f)| {
41             let ident = &f.ident.as_ref().map(|i| quote! {#i}).unwrap_or({
42                 let t = proc_macro2::Literal::usize_unsuffixed(i);
43                 quote! {#t}
44             });
45             let ty = &f.ty;
46             impl_field(ident, ty)
47         })
48         .collect();
49 
50     let gl = &generics.lt_token;
51     let gp = &generics.params;
52     let gg = &generics.gt_token;
53     let gn = gp.iter().map(|param: &syn::GenericParam| match param {
54         syn::GenericParam::Type(ref t) => {
55             let ident = &t.ident;
56             quote! { #ident }
57         }
58         p => quote! { #p },
59     });
60     let gn = quote! { #gl #( #gn ),* #gg };
61     let gw = if !gp.is_empty() {
62         let gi = gp.iter().map(|param: &syn::GenericParam| match param {
63             syn::GenericParam::Type(ref t) => {
64                 let ident = &t.ident;
65                 quote! {
66                     #ident : ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian> + ::std::convert::From<u8> + ::std::marker::Copy,
67                     ::scroll::Error : ::std::convert::From<< #ident as ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian>>::Error>,
68                     < #ident as ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian>>::Error : ::std::convert::From<scroll::Error>
69                 }
70             },
71             p => quote! { #p }
72         });
73         quote! { #( #gi ),* , }
74     } else {
75         quote! {}
76     };
77 
78     quote! {
79         impl<'a, #gp > ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian> for #name #gn where #gw #name #gn : 'a {
80             type Error = ::scroll::Error;
81             #[inline]
82             fn try_from_ctx(src: &'a [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<(Self, usize), Self::Error> {
83                 use ::scroll::Pread;
84                 let offset = &mut 0;
85                 let data  = Self { #(#items,)* };
86                 Ok((data, *offset))
87             }
88         }
89     }
90 }
91 
impl_try_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream92 fn impl_try_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
93     let name = &ast.ident;
94     let generics = &ast.generics;
95     match ast.data {
96         syn::Data::Struct(ref data) => match data.fields {
97             syn::Fields::Named(ref fields) => impl_struct(name, &fields.named, generics),
98             syn::Fields::Unnamed(ref fields) => impl_struct(name, &fields.unnamed, generics),
99             _ => {
100                 panic!("Pread can not be derived for unit structs")
101             }
102         },
103         _ => panic!("Pread can only be derived for structs"),
104     }
105 }
106 
107 #[proc_macro_derive(Pread)]
derive_pread(input: TokenStream) -> TokenStream108 pub fn derive_pread(input: TokenStream) -> TokenStream {
109     let ast: syn::DeriveInput = syn::parse(input).unwrap();
110     let gen = impl_try_from_ctx(&ast);
111     gen.into()
112 }
113 
impl_pwrite_field(ident: &proc_macro2::TokenStream, ty: &syn::Type) -> proc_macro2::TokenStream114 fn impl_pwrite_field(ident: &proc_macro2::TokenStream, ty: &syn::Type) -> proc_macro2::TokenStream {
115     match ty {
116         syn::Type::Array(ref array) => match array.len {
117             syn::Expr::Lit(syn::ExprLit {
118                 lit: syn::Lit::Int(ref int),
119                 ..
120             }) => {
121                 let size = int.base10_parse::<usize>().unwrap();
122                 quote! {
123                     for i in 0..#size {
124                         dst.gwrite_with(&self.#ident[i], offset, ctx)?;
125                     }
126                 }
127             }
128             _ => panic!("Pwrite derive with bad array constexpr"),
129         },
130         syn::Type::Group(group) => impl_pwrite_field(ident, &group.elem),
131         _ => {
132             quote! {
133                 dst.gwrite_with(&self.#ident, offset, ctx)?
134             }
135         }
136     }
137 }
138 
impl_try_into_ctx( name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, generics: &syn::Generics, ) -> proc_macro2::TokenStream139 fn impl_try_into_ctx(
140     name: &syn::Ident,
141     fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
142     generics: &syn::Generics,
143 ) -> proc_macro2::TokenStream {
144     let items: Vec<_> = fields
145         .iter()
146         .enumerate()
147         .map(|(i, f)| {
148             let ident = &f.ident.as_ref().map(|i| quote! {#i}).unwrap_or({
149                 let t = proc_macro2::Literal::usize_unsuffixed(i);
150                 quote! {#t}
151             });
152             let ty = &f.ty;
153             impl_pwrite_field(ident, ty)
154         })
155         .collect();
156 
157     let gl = &generics.lt_token;
158     let gp = &generics.params;
159     let gg = &generics.gt_token;
160     let gn = gp.iter().map(|param: &syn::GenericParam| match param {
161         syn::GenericParam::Type(ref t) => {
162             let ident = &t.ident;
163             quote! { #ident }
164         }
165         p => quote! { #p },
166     });
167     let gn = quote! { #gl #( #gn ),* #gg };
168     let gwref = if !gp.is_empty() {
169         let gi = gp.iter().map(|param: &syn::GenericParam| match param {
170             syn::GenericParam::Type(ref t) => {
171                 let ident = &t.ident;
172                 quote! {
173                     &'a #ident : ::scroll::ctx::TryIntoCtx<::scroll::Endian>,
174                     ::scroll::Error: ::std::convert::From<<&'a #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error>,
175                     <&'a #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error: ::std::convert::From<scroll::Error>
176                 }
177             },
178             p => quote! { #p }
179         });
180         quote! { where #( #gi ),* }
181     } else {
182         quote! {}
183     };
184     let gw = if !gp.is_empty() {
185         let gi = gp.iter().map(|param: &syn::GenericParam| match param {
186             syn::GenericParam::Type(ref t) => {
187                 let ident = &t.ident;
188                 quote! {
189                     #ident : ::scroll::ctx::TryIntoCtx<::scroll::Endian>,
190                     ::scroll::Error: ::std::convert::From<<#ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error>,
191                     <#ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error: ::std::convert::From<scroll::Error>
192                 }
193             },
194             p => quote! { #p }
195         });
196         quote! { where Self: ::std::marker::Copy, #( #gi ),* }
197     } else {
198         quote! {}
199     };
200 
201     quote! {
202         impl<'a, #gp > ::scroll::ctx::TryIntoCtx<::scroll::Endian> for &'a #name #gn #gwref {
203             type Error = ::scroll::Error;
204             #[inline]
205             fn try_into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<usize, Self::Error> {
206                 use ::scroll::Pwrite;
207                 let offset = &mut 0;
208                 #(#items;)*;
209                 Ok(*offset)
210             }
211         }
212 
213         impl #gl #gp #gg ::scroll::ctx::TryIntoCtx<::scroll::Endian> for #name #gn #gw {
214             type Error = ::scroll::Error;
215             #[inline]
216             fn try_into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<usize, Self::Error> {
217                 (&self).try_into_ctx(dst, ctx)
218             }
219         }
220     }
221 }
222 
impl_pwrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream223 fn impl_pwrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
224     let name = &ast.ident;
225     let generics = &ast.generics;
226     match ast.data {
227         syn::Data::Struct(ref data) => match data.fields {
228             syn::Fields::Named(ref fields) => impl_try_into_ctx(name, &fields.named, generics),
229             syn::Fields::Unnamed(ref fields) => impl_try_into_ctx(name, &fields.unnamed, generics),
230             _ => {
231                 panic!("Pwrite can not be derived for unit structs")
232             }
233         },
234         _ => panic!("Pwrite can only be derived for structs"),
235     }
236 }
237 
238 #[proc_macro_derive(Pwrite)]
derive_pwrite(input: TokenStream) -> TokenStream239 pub fn derive_pwrite(input: TokenStream) -> TokenStream {
240     let ast: syn::DeriveInput = syn::parse(input).unwrap();
241     let gen = impl_pwrite(&ast);
242     gen.into()
243 }
244 
size_with( name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, generics: &syn::Generics, ) -> proc_macro2::TokenStream245 fn size_with(
246     name: &syn::Ident,
247     fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
248     generics: &syn::Generics,
249 ) -> proc_macro2::TokenStream {
250     let items: Vec<_> = fields
251         .iter()
252         .map(|f| {
253             let ty = &f.ty;
254             match *ty {
255                 syn::Type::Array(ref array) => {
256                     let elem = &array.elem;
257                     match array.len {
258                         syn::Expr::Lit(syn::ExprLit {
259                             lit: syn::Lit::Int(ref int),
260                             ..
261                         }) => {
262                             let size = int.base10_parse::<usize>().unwrap();
263                             quote! {
264                                 (#size * <#elem>::size_with(ctx))
265                             }
266                         }
267                         _ => panic!("Pread derive with bad array constexpr"),
268                     }
269                 }
270                 _ => {
271                     quote! {
272                         <#ty>::size_with(ctx)
273                     }
274                 }
275             }
276         })
277         .collect();
278 
279     let gl = &generics.lt_token;
280     let gp = &generics.params;
281     let gg = &generics.gt_token;
282     let gn = gp.iter().map(|param: &syn::GenericParam| match param {
283         syn::GenericParam::Type(ref t) => {
284             let ident = &t.ident;
285             quote! { #ident }
286         }
287         p => quote! { #p },
288     });
289     let gn = quote! { #gl #( #gn ),* #gg };
290     let gw = if !gp.is_empty() {
291         let gi = gp.iter().map(|param: &syn::GenericParam| match param {
292             syn::GenericParam::Type(ref t) => {
293                 let ident = &t.ident;
294                 quote! {
295                     #ident : ::scroll::ctx::SizeWith<::scroll::Endian>
296                 }
297             }
298             p => quote! { #p },
299         });
300         quote! { where #( #gi ),* }
301     } else {
302         quote! {}
303     };
304 
305     quote! {
306         impl #gl #gp #gg ::scroll::ctx::SizeWith<::scroll::Endian> for #name #gn #gw {
307             #[inline]
308             fn size_with(ctx: &::scroll::Endian) -> usize {
309                 0 #(+ #items)*
310             }
311         }
312     }
313 }
314 
impl_size_with(ast: &syn::DeriveInput) -> proc_macro2::TokenStream315 fn impl_size_with(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
316     let name = &ast.ident;
317     let generics = &ast.generics;
318     match ast.data {
319         syn::Data::Struct(ref data) => match data.fields {
320             syn::Fields::Named(ref fields) => size_with(name, &fields.named, generics),
321             syn::Fields::Unnamed(ref fields) => size_with(name, &fields.unnamed, generics),
322             _ => {
323                 panic!("SizeWith can not be derived for unit structs")
324             }
325         },
326         _ => panic!("SizeWith can only be derived for structs"),
327     }
328 }
329 
330 #[proc_macro_derive(SizeWith)]
derive_sizewith(input: TokenStream) -> TokenStream331 pub fn derive_sizewith(input: TokenStream) -> TokenStream {
332     let ast: syn::DeriveInput = syn::parse(input).unwrap();
333     let gen = impl_size_with(&ast);
334     gen.into()
335 }
336 
impl_cread_struct( name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, generics: &syn::Generics, ) -> proc_macro2::TokenStream337 fn impl_cread_struct(
338     name: &syn::Ident,
339     fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
340     generics: &syn::Generics,
341 ) -> proc_macro2::TokenStream {
342     let items: Vec<_> = fields.iter().enumerate().map(|(i, f)| {
343         let ident = &f.ident.as_ref().map(|i|quote!{#i}).unwrap_or({let t = proc_macro2::Literal::usize_unsuffixed(i); quote!{#t}});
344         let ty = &f.ty;
345         match *ty {
346             syn::Type::Array(ref array) => {
347                 let arrty = &array.elem;
348                 match array.len {
349                     syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(ref int), ..}) => {
350                         let size = int.base10_parse::<usize>().unwrap();
351                         let incr = quote! { ::scroll::export::mem::size_of::<#arrty>() };
352                         quote! {
353                             #ident: {
354                                 let mut __tmp: #ty = [0u8.into(); #size];
355                                 for i in 0..__tmp.len() {
356                                     __tmp[i] = src.cread_with(*offset, ctx);
357                                     *offset += #incr;
358                                 }
359                                 __tmp
360                             }
361                         }
362                     },
363                     _ => panic!("IOread derive with bad array constexpr")
364                 }
365             },
366             _ => {
367                 let size = quote! { ::scroll::export::mem::size_of::<#ty>() };
368                 quote! {
369                     #ident: { let res = src.cread_with::<#ty>(*offset, ctx); *offset += #size; res }
370                 }
371             }
372         }
373     }).collect();
374 
375     let gl = &generics.lt_token;
376     let gp = &generics.params;
377     let gg = &generics.gt_token;
378     let gn = gp.iter().map(|param: &syn::GenericParam| match param {
379         syn::GenericParam::Type(ref t) => {
380             let ident = &t.ident;
381             quote! { #ident }
382         }
383         p => quote! { #p },
384     });
385     let gn = quote! { #gl #( #gn ),* #gg };
386     let gw = if !gp.is_empty() {
387         let gi = gp.iter().map(|param: &syn::GenericParam| match param {
388             syn::GenericParam::Type(ref t) => {
389                 let ident = &t.ident;
390                 quote! {
391                     #ident : ::scroll::ctx::FromCtx<::scroll::Endian> + ::std::convert::From<u8> + ::std::marker::Copy
392                 }
393             },
394             p => quote! { #p }
395         });
396         quote! { where #( #gi ),* , }
397     } else {
398         quote! {}
399     };
400 
401     quote! {
402         impl #gl #gp #gg ::scroll::ctx::FromCtx<::scroll::Endian> for #name #gn #gw {
403             #[inline]
404             fn from_ctx(src: &[u8], ctx: ::scroll::Endian) -> Self {
405                 use ::scroll::Cread;
406                 let offset = &mut 0;
407                 let data = Self { #(#items,)* };
408                 data
409             }
410         }
411     }
412 }
413 
impl_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream414 fn impl_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
415     let name = &ast.ident;
416     let generics = &ast.generics;
417     match ast.data {
418         syn::Data::Struct(ref data) => match data.fields {
419             syn::Fields::Named(ref fields) => impl_cread_struct(name, &fields.named, generics),
420             syn::Fields::Unnamed(ref fields) => impl_cread_struct(name, &fields.unnamed, generics),
421             _ => {
422                 panic!("IOread can not be derived for unit structs")
423             }
424         },
425         _ => panic!("IOread can only be derived for structs"),
426     }
427 }
428 
429 #[proc_macro_derive(IOread)]
derive_ioread(input: TokenStream) -> TokenStream430 pub fn derive_ioread(input: TokenStream) -> TokenStream {
431     let ast: syn::DeriveInput = syn::parse(input).unwrap();
432     let gen = impl_from_ctx(&ast);
433     gen.into()
434 }
435 
impl_into_ctx( name: &syn::Ident, fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, generics: &syn::Generics, ) -> proc_macro2::TokenStream436 fn impl_into_ctx(
437     name: &syn::Ident,
438     fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
439     generics: &syn::Generics,
440 ) -> proc_macro2::TokenStream {
441     let items: Vec<_> = fields
442         .iter()
443         .enumerate()
444         .map(|(i, f)| {
445             let ident = &f.ident.as_ref().map(|i| quote! {#i}).unwrap_or({
446                 let t = proc_macro2::Literal::usize_unsuffixed(i);
447                 quote! {#t}
448             });
449             let ty = &f.ty;
450             let size = quote! { ::scroll::export::mem::size_of::<#ty>() };
451             match *ty {
452                 syn::Type::Array(ref array) => {
453                     let arrty = &array.elem;
454                     quote! {
455                         let size = ::scroll::export::mem::size_of::<#arrty>();
456                         for i in 0..self.#ident.len() {
457                             dst.cwrite_with(self.#ident[i], *offset, ctx);
458                             *offset += size;
459                         }
460                     }
461                 }
462                 _ => {
463                     quote! {
464                         dst.cwrite_with(self.#ident, *offset, ctx);
465                         *offset += #size;
466                     }
467                 }
468             }
469         })
470         .collect();
471 
472     let gl = &generics.lt_token;
473     let gp = &generics.params;
474     let gg = &generics.gt_token;
475     let gn = gp.iter().map(|param: &syn::GenericParam| match param {
476         syn::GenericParam::Type(ref t) => {
477             let ident = &t.ident;
478             quote! { #ident }
479         }
480         p => quote! { #p },
481     });
482     let gw = if !gp.is_empty() {
483         let gi = gp.iter().map(|param: &syn::GenericParam| match param {
484             syn::GenericParam::Type(ref t) => {
485                 let ident = &t.ident;
486                 quote! {
487                     #ident : ::scroll::ctx::IntoCtx<::scroll::Endian> + ::std::marker::Copy
488                 }
489             }
490             p => quote! { #p },
491         });
492         quote! { where #( #gi ),* }
493     } else {
494         quote! {}
495     };
496     let gn = quote! { #gl #( #gn ),* #gg };
497 
498     quote! {
499         impl<'a, #gp > ::scroll::ctx::IntoCtx<::scroll::Endian> for &'a #name #gn #gw {
500             #[inline]
501             fn into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) {
502                 use ::scroll::Cwrite;
503                 let offset = &mut 0;
504                 #(#items;)*;
505                 ()
506             }
507         }
508 
509         impl #gl #gp #gg ::scroll::ctx::IntoCtx<::scroll::Endian> for #name #gn #gw {
510             #[inline]
511             fn into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) {
512                 (&self).into_ctx(dst, ctx)
513             }
514         }
515     }
516 }
517 
impl_iowrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream518 fn impl_iowrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
519     let name = &ast.ident;
520     let generics = &ast.generics;
521     match ast.data {
522         syn::Data::Struct(ref data) => match data.fields {
523             syn::Fields::Named(ref fields) => impl_into_ctx(name, &fields.named, generics),
524             syn::Fields::Unnamed(ref fields) => impl_into_ctx(name, &fields.unnamed, generics),
525             _ => {
526                 panic!("IOwrite can not be derived for unit structs")
527             }
528         },
529         _ => panic!("IOwrite can only be derived for structs"),
530     }
531 }
532 
533 #[proc_macro_derive(IOwrite)]
derive_iowrite(input: TokenStream) -> TokenStream534 pub fn derive_iowrite(input: TokenStream) -> TokenStream {
535     let ast: syn::DeriveInput = syn::parse(input).unwrap();
536     let gen = impl_iowrite(&ast);
537     gen.into()
538 }
539