1#![recursion_limit = "1024"]
2
3extern crate proc_macro;
4
5use proc_macro2::Span;
6use quote::{ToTokens, format_ident, quote};
7use syn::Ident;
8
9fn extract_idents_and_offset(
10 fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>,
11) -> (Vec<(proc_macro2::TokenStream, &syn::Field)>, syn::Ident) {
12 let idents: Vec<_> = fields
14 .into_iter()
15 .enumerate()
16 .map(|(i, f)| {
17 let ident = f.ident.as_ref().map(|i| quote! {#i}).unwrap_or({
18 let t = proc_macro2::Literal::usize_unsuffixed(i);
19 quote! {#t}
20 });
21 (ident, f)
22 })
23 .collect();
24 let offset = fresh_name(
26 &fields,
27 proc_macro2::Ident::new("offset", Span::call_site()),
28 );
29
30 (idents, offset)
31}
32
33fn fresh_name(
38 fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>,
39 mut target: proc_macro2::Ident,
40) -> Ident {
41 while fields.iter().any(|f| {
42 f.ident
43 .as_ref()
44 .map(|ident| ident == &target)
45 .unwrap_or(false)
46 }) {
47 target = format_ident!("_{target}");
48 }
49 target
50}
51
52fn extract_lifetime(
53 gp: &syn::punctuated::Punctuated<syn::GenericParam, syn::token::Comma>,
54) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
55 let mut lifetimes = gp
56 .iter()
57 .filter_map(|param: &syn::GenericParam| match param {
58 syn::GenericParam::Lifetime(lifetime) => Some(lifetime.lifetime.clone()),
59 _ => None,
60 })
61 .collect::<Vec<_>>();
62 if lifetimes.len() > 1 {
63 panic!("Pread cannot be derived for multiple lifetimes")
64 }
65 let lifetime = lifetimes
66 .pop()
67 .unwrap_or(syn::Lifetime::new("'a", Span::call_site()));
68 let alpha = format!("'{}_fresh", lifetime.ident.to_string());
70 (
71 lifetime.to_token_stream(),
72 syn::Lifetime::new(&alpha.to_string(), lifetime.span()).to_token_stream(),
73 )
74}
75
76fn impl_field(
77 ident: &proc_macro2::TokenStream,
78 ty: &syn::Type,
79 src: &proc_macro2::Ident,
80 default_ctx: &proc_macro2::TokenStream,
81 custom_ctx: Option<&proc_macro2::TokenStream>,
82 offset: &Ident,
83 noctx: bool,
84) -> proc_macro2::TokenStream {
85 let ctx = custom_ctx.unwrap_or(default_ctx);
86 match ty {
87 syn::Type::Group(group) => impl_field(
88 ident,
89 &group.elem,
90 src,
91 default_ctx,
92 custom_ctx,
93 offset,
94 noctx,
95 ),
96 _ => {
97 if noctx {
98 quote! {
99 let #ident = #src.gread::<#ty>(#offset)?;
100 }
101 } else {
102 quote! {
103 let #ident = #src.gread_with::<#ty>(#offset, #ctx)?;
104 }
105 }
106 }
107 }
108}
109
110fn get_attr<'a>(attr_ident: &str, field: &'a syn::Field) -> Option<&'a syn::Attribute> {
116 field
117 .attrs
118 .iter()
119 .find(|attr| attr.path().is_ident(attr_ident))
120}
121
122fn custom_ctx(field: &syn::Field, noctx: &mut bool) -> Option<proc_macro2::TokenStream> {
128 get_attr("scroll", field).and_then(|x| {
129 let mut attrib = None;
132 let res = x.parse_nested_meta(|meta| {
133 if meta.path.is_ident("ctx") {
135 let value = meta.value()?; attrib = Some(value.parse::<syn::Expr>()?.into_token_stream()); return Ok(());
139 }
140 if meta.path.is_ident("noctx") {
141 *noctx = true;
143 return Ok(());
144 }
145 Err(meta.error(match meta.path.get_ident() {
146 Some(ident) => format!("unrecognized attribute: {ident}"),
147 None => "unrecognized and invalid attribute".to_owned(),
148 }))
149 });
150 match res {
151 Ok(()) => attrib,
152 Err(e) => Some(e.into_compile_error()),
153 }
154 })
155}
156
157fn impl_struct(
158 name: &syn::Ident,
159 fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
160 generics: &syn::Generics,
161 unnamed: bool,
162) -> proc_macro2::TokenStream {
163 let offset = fresh_name(
164 fields,
165 syn::Ident::new("offset", proc_macro2::Span::call_site()),
166 );
167 let src = fresh_name(
168 fields,
169 syn::Ident::new("src", proc_macro2::Span::call_site()),
170 );
171 let ctx = fresh_name(
172 fields,
173 syn::Ident::new("ctx", proc_macro2::Span::call_site()),
174 )
175 .to_token_stream();
176 let (items, item_assignments) = fields
177 .iter()
178 .enumerate()
179 .map(|(i, f)| {
180 let (ident, prefixed_ident) = &f
181 .ident
182 .as_ref()
183 .map(|i| (quote! {#i}, quote! {#i}))
184 .unwrap_or({
185 let t = proc_macro2::Literal::usize_unsuffixed(i);
186 let suf = if unnamed {
187 syn::Ident::new(&format!("_{t}"), proc_macro2::Span::call_site())
188 .into_token_stream()
189 } else {
190 t.clone().to_token_stream()
191 };
192 (quote! {#t}, suf)
193 });
194 let ty = &f.ty;
195 let mut noctx = false;
197 let custom_ctx = custom_ctx(f, &mut noctx);
198 (
199 impl_field(
200 &prefixed_ident,
201 ty,
202 &src,
203 &ctx,
204 custom_ctx.as_ref(),
205 &offset,
206 noctx,
207 ),
208 quote! { #ident: #prefixed_ident },
209 )
210 })
211 .collect::<(Vec<_>, Vec<_>)>();
212
213 let gl = &generics.lt_token;
214 let gp = &generics.params;
215 let gg = &generics.gt_token;
216 let gn = gp.iter().map(|param: &syn::GenericParam| match param {
217 syn::GenericParam::Type(t) => {
218 let ident = &t.ident;
219 quote! { #ident }
220 }
221 p => quote! { #p },
222 });
223
224 let (lifetime, _fresh_lifetime) = extract_lifetime(gp);
225 let gn = quote! { #gl #( #gn ),* #gg };
226 let initial_generic_params = gp
228 .iter()
229 .filter_map(|param: &syn::GenericParam| match param {
230 syn::GenericParam::Lifetime(_) => None,
231 p => Some(p),
232 })
233 .collect::<Vec<_>>();
234 let lhs_gp = if !initial_generic_params.is_empty() {
235 quote! { #( #initial_generic_params ),* }
236 } else {
237 quote! {}
238 };
239
240 let gw = if !gp.is_empty() {
241 let gi = gp.iter().filter_map(|param: &syn::GenericParam| match param {
242 syn::GenericParam::Type(t) => Some({
243 let ident = &t.ident;
244 quote! {
245 #ident : ::scroll::ctx::TryFromCtx<#lifetime, ::scroll::Endian, Error = ::scroll::Error>,
246 ::scroll::Error : ::std::convert::From<< #ident as ::scroll::ctx::TryFromCtx<#lifetime, ::scroll::Endian>>::Error>,
247 < #ident as ::scroll::ctx::TryFromCtx<#lifetime, ::scroll::Endian>>::Error : ::std::convert::From<scroll::Error>
248 }
249 }),
250 syn::GenericParam::Lifetime(_) => None,
251 p => Some(quote! { #p })
252 }).collect::<Vec<_>>();
253 if !gi.is_empty() {
254 quote! { #( #gi ),*, }
256 } else {
257 quote! {}
258 }
259 } else {
260 quote! {}
261 };
262
263 quote! {
264 impl<#lifetime, #lhs_gp > ::scroll::ctx::TryFromCtx<#lifetime, ::scroll::Endian> for #name #gn
265 where #gw #name #gn : #lifetime {
266 type Error = ::scroll::Error;
268 #[inline]
269 fn try_from_ctx(#src: &#lifetime [u8], #ctx: ::scroll::Endian) -> ::scroll::export::result::Result<(Self, usize), Self::Error> {
270 use ::scroll::Pread;
271 let #offset = &mut 0;
272 #(#items)*
273 Ok((Self { #(#item_assignments,)* }, *#offset))
274 }
275 }
276 }
277}
278
279fn ensure_fieldless(variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>) {
280 for variant in variants {
281 if !variant.fields.is_empty() {
282 panic!("Deriving enums in scroll must be primitive, fieldless enums");
283 }
284 }
285}
286
287const VALID_PRIMITIVE_REPRS: &[&'static str] = &[
288 "i8", "i16", "i32", "i64", "i128", "u8", "u16", "u32", "u64", "u128",
289];
290
291fn extract_repr_type(ast: &syn::DeriveInput) -> syn::Ident {
292 let mut repr_type: Option<syn::Ident> = None;
293 for attr in &ast.attrs {
294 if attr.path().is_ident("repr") {
295 let _ = attr.parse_nested_meta(|meta| {
296 for prim in VALID_PRIMITIVE_REPRS {
297 if meta.path.is_ident(prim) {
298 repr_type = meta.path.get_ident().cloned();
299 return Ok(());
300 }
301 }
302 Ok(())
303 });
304 };
305 }
306 let Some(repr_type) = repr_type else {
307 panic!("Deriving pread on enum requires repr with one of: {VALID_PRIMITIVE_REPRS:?}");
308 };
309 repr_type
310}
311
312fn impl_try_from_ctx_enum(
313 name: &syn::Ident,
314 repr_type: syn::Ident,
315 variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>,
316) -> proc_macro2::TokenStream {
317 let variant_consts = variants.iter().map(|variant| {
318 let ident = &variant.ident;
319 let const_name = format_ident!("_{}", ident.to_string().to_uppercase());
320 quote! {
321 const #const_name: #repr_type = #name::#ident as #repr_type;
322 }
323 });
324 let variant_cases = variants.iter().map(|variant| {
325 let ident = &variant.ident;
326 let const_name = format_ident!("_{}", ident.to_string().to_uppercase());
327 quote! {
328 #const_name => #name::#ident,
329 }
330 });
331 let static_msg = format!(
332 "No variants matched a discriminant of type {}",
333 repr_type.to_string()
334 );
335 quote! {
336 impl<'a> ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian> for #name {
337 type Error = ::scroll::Error;
338 #[inline]
339 fn try_from_ctx(src: &'a [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<(Self, usize), Self::Error> {
340 use ::scroll::Pread;
341 #(#variant_consts)*
342 let offset = &mut 0;
343 let val = match src.gread_with::<#repr_type>(offset, ctx)? {
344 #(#variant_cases)*
345 _ => return Err(::scroll::Error::BadInput { size: *offset, msg: #static_msg})
346 };
347 Ok((val, *offset))
348 }
349 }
350 }
351}
352
353fn validate_enum(ast: &syn::DeriveInput, data: &syn::DataEnum) -> Ident {
354 let repr_type = extract_repr_type(ast);
355 ensure_fieldless(&data.variants);
356 repr_type
357}
358
359fn impl_try_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
360 let name = &ast.ident;
361 let generics = &ast.generics;
362 match &ast.data {
363 syn::Data::Struct(data) => match &data.fields {
364 syn::Fields::Named(fields) => impl_struct(name, &fields.named, generics, false),
365 syn::Fields::Unnamed(fields) => impl_struct(name, &fields.unnamed, generics, true),
366 _ => {
367 panic!("Pread can not be derived for unit structs")
368 }
369 },
370 syn::Data::Enum(data) => {
371 let repr_type = validate_enum(ast, data);
372 impl_try_from_ctx_enum(&ast.ident, repr_type, &data.variants)
373 }
374 _ => panic!("Pread can only be derived for structs and primitive enums"),
375 }
376}
377
378#[proc_macro_derive(Pread, attributes(scroll))]
379pub fn derive_pread(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
380 let ast: syn::DeriveInput = syn::parse(input).unwrap();
381 let generated = impl_try_from_ctx(&ast);
382 generated.into()
383}
384
385fn impl_pwrite_field(
386 ident: &proc_macro2::TokenStream,
387 ty: &syn::Type,
388 default_ctx: &proc_macro2::TokenStream,
389 custom_ctx: Option<&proc_macro2::TokenStream>,
390 offset: &proc_macro2::Ident,
391 noctx: bool,
392) -> proc_macro2::TokenStream {
393 let ctx = custom_ctx.unwrap_or(default_ctx);
394 match ty {
395 syn::Type::Array(array) => match &array.len {
396 syn::Expr::Lit(syn::ExprLit {
397 lit: syn::Lit::Int(int),
398 ..
399 }) => {
400 let size = int.base10_parse::<usize>().unwrap();
401 quote! {
402 for i in 0..#size {
403 dst.gwrite_with(&self.#ident[i], #offset, #ctx)?;
404 }
405 }
406 }
407 _ => panic!("Pwrite derive with bad array constexpr"),
408 },
409 syn::Type::Group(group) => {
410 impl_pwrite_field(ident, &group.elem, default_ctx, custom_ctx, offset, noctx)
411 }
412 syn::Type::Reference(reference) => match *reference.elem {
413 syn::Type::Slice(_) => {
414 quote! {
415 dst.gwrite_with(self.#ident, #offset, ())?
416 }
417 }
418 syn::Type::Path(ref path) => {
419 if path.path.get_ident().unwrap().to_string().as_str() == "str" {
420 quote! {
421 dst.gwrite(self.#ident, #offset)?
422 }
423 } else {
424 quote! {
425 dst.gwrite_with(self.#ident, #offset, #ctx)?
426 }
427 }
428 }
429 _ => {
430 quote! {
431 dst.gwrite_with(self.#ident, #offset, #ctx)?
432 }
433 }
434 },
435 _ => {
436 quote! {
437 dst.gwrite_with(&self.#ident, #offset, #ctx)?
438 }
439 }
440 }
441}
442
443fn impl_try_into_ctx(
444 name: &syn::Ident,
445 fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
446 generics: &syn::Generics,
447) -> proc_macro2::TokenStream {
448 let (idents, offset) = extract_idents_and_offset(fields);
449 let ctx = fresh_name(
450 fields,
451 syn::Ident::new("ctx", proc_macro2::Span::call_site()),
452 )
453 .to_token_stream();
454 let items: Vec<_> = idents
455 .iter()
456 .map(|(ident, f)| {
457 let ty = &f.ty;
458 let mut noctx = false;
459 let custom_ctx = custom_ctx(f, &mut noctx);
460 impl_pwrite_field(ident, ty, &ctx, custom_ctx.as_ref(), &offset, noctx)
461 })
462 .collect();
463
464 let gl = &generics.lt_token;
465 let gp = &generics.params;
466 let gg = &generics.gt_token;
467 let gn = gp.iter().map(|param: &syn::GenericParam| match param {
468 syn::GenericParam::Type(t) => {
469 let ident = &t.ident;
470 quote! { #ident }
471 }
472 p => quote! { #p },
473 });
474 let gn = quote! { #gl #( #gn ),* #gg };
475 let (_lifetime, fresh_lifetime) = extract_lifetime(gp);
477 let gwref = if !gp.is_empty() {
478 let gi: Vec<_> = gp.iter().filter_map(|param: &syn::GenericParam| match param {
479 syn::GenericParam::Type(t) => {
480 let ident = &t.ident;
481 Some(quote! {
482 &#fresh_lifetime #ident : ::scroll::ctx::TryIntoCtx<::scroll::Endian>,
483 ::scroll::Error: ::std::convert::From<<&#fresh_lifetime #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error>,
484 <&#fresh_lifetime #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error: ::std::convert::From<scroll::Error>
485 })
486 },
487 syn::GenericParam::Lifetime(_) => None,
488 p => Some(quote! { #p }),
489 }).collect();
490 if !gi.is_empty() {
491 quote! { where #( #gi ),* }
492 } else {
493 quote! {}
494 }
495 } else {
496 quote! {}
497 };
498 let gw = if !gp.is_empty() {
499 let gi = gp.iter().filter_map(|param: &syn::GenericParam| match param {
500 syn::GenericParam::Type(t) => {
501 let ident = &t.ident;
502 Some(quote! {
503 #ident : ::scroll::ctx::TryIntoCtx<::scroll::Endian>,
504 ::scroll::Error: ::std::convert::From<<#ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error>,
505 <#ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error: ::std::convert::From<scroll::Error>
506 })
507 },
508 syn::GenericParam::Lifetime(_) => None,
509 p => Some(quote! { #p }),
510 });
511 quote! { where Self: ::std::marker::Copy, #( #gi ),* }
512 } else {
513 quote! {}
514 };
515
516 quote! {
517 impl<#fresh_lifetime, #gp > ::scroll::ctx::TryIntoCtx<::scroll::Endian> for &#fresh_lifetime #name #gn #gwref {
518 type Error = ::scroll::Error;
519 #[inline]
520 fn try_into_ctx(self, dst: &mut [u8], #ctx: ::scroll::Endian) -> ::scroll::export::result::Result<usize, Self::Error> {
521 use ::scroll::Pwrite;
522 let #offset = &mut 0;
523 #(#items;)*
524 Ok(*#offset)
525 }
526 }
527
528 impl #gl #gp #gg ::scroll::ctx::TryIntoCtx<::scroll::Endian> for #name #gn #gw {
529 type Error = ::scroll::Error;
530 #[inline]
531 fn try_into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<usize, Self::Error> {
532 (&self).try_into_ctx(dst, ctx)
533 }
534 }
535 }
536}
537
538fn impl_try_into_ctx_primitive_enum(
539 name: &Ident,
540 repr_type: Ident,
541 _variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>,
542) -> proc_macro2::TokenStream {
543 quote! {
544 impl ::scroll::ctx::TryIntoCtx<::scroll::Endian> for &'_ #name {
545 type Error = ::scroll::Error;
546 #[inline]
547 fn try_into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<usize, Self::Error> {
548 use ::scroll::Pwrite;
549 dst.pwrite_with(unsafe { *<*const _>::from(self).cast::<#repr_type>() }, 0, ctx)
554 }
555 }
556
557 impl ::scroll::ctx::TryIntoCtx<::scroll::Endian> for #name {
558 type Error = ::scroll::Error;
559 #[inline]
560 fn try_into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<usize, Self::Error> {
561 (&self).try_into_ctx(dst, ctx)
562 }
563 }
564 }
565}
566
567fn impl_pwrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
568 let name = &ast.ident;
569 let generics = &ast.generics;
570 match &ast.data {
571 syn::Data::Struct(data) => match &data.fields {
572 syn::Fields::Named(fields) => impl_try_into_ctx(name, &fields.named, generics),
573 syn::Fields::Unnamed(fields) => impl_try_into_ctx(name, &fields.unnamed, generics),
574 _ => {
575 panic!("Pwrite can not be derived for unit structs")
576 }
577 },
578 syn::Data::Enum(data) => {
579 let repr_type = validate_enum(ast, data);
580 impl_try_into_ctx_primitive_enum(&ast.ident, repr_type, &data.variants)
581 }
582 _ => panic!("Pwrite can only be derived for structs and primitive enums"),
583 }
584}
585
586#[proc_macro_derive(Pwrite, attributes(scroll))]
587pub fn derive_pwrite(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
588 let ast: syn::DeriveInput = syn::parse(input).unwrap();
589 let generated = impl_pwrite(&ast);
590 generated.into()
591}
592
593fn size_with(
594 name: &syn::Ident,
595 fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
596 generics: &syn::Generics,
597) -> proc_macro2::TokenStream {
598 let items: Vec<_> = fields
599 .iter()
600 .map(|f| {
601 let ty = &f.ty;
602 let mut noctx = false;
603 let custom_ctx = custom_ctx(f, &mut noctx).map(|x| quote! {&#x});
604 let default_ctx =
605 syn::Ident::new("ctx", proc_macro2::Span::call_site()).into_token_stream();
606 let ctx = custom_ctx.unwrap_or(default_ctx);
607 match ty {
608 syn::Type::Reference(_) => {
609 panic!("SizeWith cannot be derived for references")
610 }
611 syn::Type::Array(array) => {
612 let elem = &array.elem;
613 match &array.len {
614 syn::Expr::Lit(syn::ExprLit {
615 lit: syn::Lit::Int(int),
616 ..
617 }) => {
618 let size = int.base10_parse::<usize>().unwrap();
619 quote! {
620 (#size * <#elem>::size_with(#ctx))
621 }
622 }
623 _ => panic!("SizeWith derive has bad array constexpr"),
624 }
625 }
626 _ => {
627 quote! {
628 <#ty>::size_with(#ctx)
629 }
630 }
631 }
632 })
633 .collect();
634
635 let gl = &generics.lt_token;
636 let gp = &generics.params;
637 let gg = &generics.gt_token;
638 let gn = gp.iter().map(|param: &syn::GenericParam| match param {
639 syn::GenericParam::Type(t) => {
640 let ident = &t.ident;
641 quote! { #ident }
642 }
643 p => quote! { #p },
644 });
645 let gn = quote! { #gl #( #gn ),* #gg };
646 let gw = if !gp.is_empty() {
647 let gi = gp
648 .iter()
649 .filter_map(|param: &syn::GenericParam| match param {
650 syn::GenericParam::Type(t) => {
651 let ident = &t.ident;
652 Some(quote! {
653 #ident : ::scroll::ctx::SizeWith<::scroll::Endian>
654 })
655 }
656 syn::GenericParam::Lifetime(_) => None,
657 p => Some(quote! { #p }),
658 });
659 quote! { where #( #gi ),* }
660 } else {
661 quote! {}
662 };
663
664 quote! {
665 impl #gl #gp #gg ::scroll::ctx::SizeWith<::scroll::Endian> for #name #gn #gw {
666 #[inline]
667 fn size_with(ctx: &::scroll::Endian) -> usize {
668 0 #(+ #items)*
669 }
670 }
671 }
672}
673
674fn impl_size_with(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
675 let name = &ast.ident;
676 let generics = &ast.generics;
677 match &ast.data {
678 syn::Data::Struct(data) => match &data.fields {
679 syn::Fields::Named(fields) => size_with(name, &fields.named, generics),
680 syn::Fields::Unnamed(fields) => size_with(name, &fields.unnamed, generics),
681 _ => {
682 panic!("SizeWith can not be derived for unit structs")
683 }
684 },
685 _ => panic!("SizeWith can only be derived for structs"),
686 }
687}
688
689#[proc_macro_derive(SizeWith, attributes(scroll))]
690pub fn derive_sizewith(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
691 let ast: syn::DeriveInput = syn::parse(input).unwrap();
692 let generated = impl_size_with(&ast);
693 generated.into()
694}
695
696fn impl_cread_struct(
697 name: &syn::Ident,
698 fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
699 generics: &syn::Generics,
700) -> proc_macro2::TokenStream {
701 let items: Vec<_> = fields.iter().enumerate().map(|(i, f)| {
702 let ident = &f.ident.as_ref().map(|i|quote!{#i}).unwrap_or({let t = proc_macro2::Literal::usize_unsuffixed(i); quote!{#t}});
703 let ty = &f.ty;
704 let mut noctx = false;
705 let custom_ctx = custom_ctx(f, &mut noctx);
706 let default_ctx =
707 syn::Ident::new("ctx", proc_macro2::Span::call_site()).into_token_stream();
708 let ctx = custom_ctx.unwrap_or(default_ctx);
709 match ty {
710 syn::Type::Reference(_) => {
711 panic!("IOread cannot be derived for references, because SizeWith cannot be derived for references")
712 }
713 syn::Type::Array(array) => {
714 let arrty = &array.elem;
715 match &array.len {
716 syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(int), ..}) => {
717 let size = int.base10_parse::<usize>().unwrap();
718 let incr = quote! { ::scroll::export::mem::size_of::<#arrty>() };
719 quote! {
720 #ident: {
721 let mut __tmp: #ty = [0u8.into(); #size];
722 for i in 0..__tmp.len() {
723 __tmp[i] = src.cread_with(*offset, #ctx);
724 *offset += #incr;
725 }
726 __tmp
727 }
728 }
729 },
730 _ => panic!("IOread derive with bad array constexpr")
731 }
732 },
733 _ => {
734 let size = quote! { ::scroll::export::mem::size_of::<#ty>() };
735 quote! {
736 #ident: { let res = src.cread_with::<#ty>(*offset, #ctx); *offset += #size; res }
737 }
738 }
739 }
740 }).collect();
741
742 let gl = &generics.lt_token;
743 let gp = &generics.params;
744 let gg = &generics.gt_token;
745 let gn = gp.iter().map(|param: &syn::GenericParam| match param {
746 syn::GenericParam::Type(t) => {
747 let ident = &t.ident;
748 quote! { #ident }
749 }
750 p => quote! { #p },
751 });
752 let gn = quote! { #gl #( #gn ),* #gg };
753 let gw = if !gp.is_empty() {
754 let gi = gp.iter().map(|param: &syn::GenericParam| match param {
755 syn::GenericParam::Type(t) => {
756 let ident = &t.ident;
757 quote! {
758 #ident : ::scroll::ctx::FromCtx<::scroll::Endian> + ::std::convert::From<u8> + ::std::marker::Copy
759 }
760 },
761 p => quote! { #p }
762 });
763 quote! { where #( #gi ),* , }
764 } else {
765 quote! {}
766 };
767
768 quote! {
769 impl #gl #gp #gg ::scroll::ctx::FromCtx<::scroll::Endian> for #name #gn #gw {
770 #[inline]
771 fn from_ctx(src: &[u8], ctx: ::scroll::Endian) -> Self {
772 use ::scroll::Cread;
773 let offset = &mut 0;
774 let data = Self { #(#items,)* };
775 data
776 }
777 }
778 }
779}
780
781fn impl_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
782 let name = &ast.ident;
783 let generics = &ast.generics;
784 match &ast.data {
785 syn::Data::Struct(data) => match &data.fields {
786 syn::Fields::Named(fields) => impl_cread_struct(name, &fields.named, generics),
787 syn::Fields::Unnamed(fields) => impl_cread_struct(name, &fields.unnamed, generics),
788 _ => {
789 panic!("IOread can not be derived for unit structs")
790 }
791 },
792 _ => panic!("IOread can only be derived for structs"),
793 }
794}
795
796#[proc_macro_derive(IOread, attributes(scroll))]
797pub fn derive_ioread(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
798 let ast: syn::DeriveInput = syn::parse(input).unwrap();
799 let generated = impl_from_ctx(&ast);
800 generated.into()
801}
802
803fn impl_into_ctx(
804 name: &syn::Ident,
805 fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
806 generics: &syn::Generics,
807) -> proc_macro2::TokenStream {
808 let items: Vec<_> = fields
809 .iter()
810 .enumerate()
811 .map(|(i, f)| {
812 let ident = &f.ident.as_ref().map(|i| quote! {#i}).unwrap_or({
813 let t = proc_macro2::Literal::usize_unsuffixed(i);
814 quote! {#t}
815 });
816 let ty = &f.ty;
817 let mut noctx = false;
818 let size = quote! { ::scroll::export::mem::size_of::<#ty>() };
819 let custom_ctx = custom_ctx(f, &mut noctx);
820 let default_ctx =
821 syn::Ident::new("ctx", proc_macro2::Span::call_site()).into_token_stream();
822 let ctx = custom_ctx.unwrap_or(default_ctx);
823 match ty {
824 syn::Type::Reference(_) => {
825 panic!("IOwrite cannot be derived for references, because SizeWith cannot be derived for references")
826 }
827 syn::Type::Array(array) => {
828 let arrty = &array.elem;
829 quote! {
830 let size = ::scroll::export::mem::size_of::<#arrty>();
831 for i in 0..self.#ident.len() {
832 dst.cwrite_with(self.#ident[i], *offset, #ctx);
833 *offset += size;
834 }
835 }
836 }
837 _ => {
838 quote! {
839 dst.cwrite_with(self.#ident, *offset, #ctx);
840 *offset += #size;
841 }
842 }
843 }
844 })
845 .collect();
846
847 let gl = &generics.lt_token;
848 let gp = &generics.params;
849 let gg = &generics.gt_token;
850 let gn = gp.iter().map(|param: &syn::GenericParam| match param {
851 syn::GenericParam::Type(t) => {
852 let ident = &t.ident;
853 quote! { #ident }
854 }
855 p => quote! { #p },
856 });
857 let gw = if !gp.is_empty() {
858 let gi = gp.iter().map(|param: &syn::GenericParam| match param {
859 syn::GenericParam::Type(t) => {
860 let ident = &t.ident;
861 quote! {
862 #ident : ::scroll::ctx::IntoCtx<::scroll::Endian> + ::std::marker::Copy
863 }
864 }
865 p => quote! { #p },
866 });
867 quote! { where #( #gi ),* }
868 } else {
869 quote! {}
870 };
871 let gn = quote! { #gl #( #gn ),* #gg };
872
873 quote! {
874 impl<'a, #gp > ::scroll::ctx::IntoCtx<::scroll::Endian> for &'a #name #gn #gw {
875 #[inline]
876 fn into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) {
877 use ::scroll::Cwrite;
878 let offset = &mut 0;
879 #(#items;)*;
880 }
881 }
882
883 impl #gl #gp #gg ::scroll::ctx::IntoCtx<::scroll::Endian> for #name #gn #gw {
884 #[inline]
885 fn into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) {
886 (&self).into_ctx(dst, ctx)
887 }
888 }
889 }
890}
891
892fn impl_iowrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
893 let name = &ast.ident;
894 let generics = &ast.generics;
895 match &ast.data {
896 syn::Data::Struct(data) => match &data.fields {
897 syn::Fields::Named(fields) => impl_into_ctx(name, &fields.named, generics),
898 syn::Fields::Unnamed(fields) => impl_into_ctx(name, &fields.unnamed, generics),
899 _ => {
900 panic!("IOwrite can not be derived for unit structs")
901 }
902 },
903 _ => panic!("IOwrite can only be derived for structs"),
904 }
905}
906
907#[proc_macro_derive(IOwrite, attributes(scroll))]
908pub fn derive_iowrite(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
909 let ast: syn::DeriveInput = syn::parse(input).unwrap();
910 let generated = impl_iowrite(&ast);
911 generated.into()
912}