ab_io_type_derive/lib.rs
1use proc_macro2::{Ident, Literal, Span, TokenStream};
2use quote::{format_ident, quote, quote_spanned};
3use std::iter;
4use syn::spanned::Spanned;
5use syn::{
6 Data, DataEnum, DataStruct, DeriveInput, Error, ExprPath, Fields, Type, parse_macro_input,
7};
8
9/// Derive macro for `TrivialType` trait of `ab-io-type` crate.
10///
11/// See the documentation of the trait for supported types and requirements, which are checked at
12/// compile time.
13#[proc_macro_derive(TrivialType, attributes(trivial_type))]
14pub fn trivial_type_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
15 let input = parse_macro_input!(input as DeriveInput);
16
17 if !input.generics.params.is_empty() {
18 return Error::new(
19 input.ident.span(),
20 "`TrivialType` can't be derived on generic types",
21 )
22 .into_compile_error()
23 .into();
24 }
25
26 let validate = match parse_validate(&input) {
27 Ok(validate) => validate,
28 Err(error) => {
29 return error.to_compile_error().into();
30 }
31 };
32
33 let type_name = &input.ident;
34
35 let output = match &input.data {
36 Data::Struct(data_struct) => {
37 if let Err(error) = check_repr(
38 &input,
39 &["C", "transparent"],
40 "`TrivialType` on structs requires `#[repr(C)]` or `#[repr(transparent)]` without \
41 other options",
42 ) {
43 return error.to_compile_error().into();
44 }
45 let field_types = data_struct
46 .fields
47 .iter()
48 .map(|field| &field.ty)
49 .collect::<Vec<_>>();
50
51 let struct_metadata = match generate_struct_metadata(type_name, data_struct) {
52 Ok(struct_metadata) => struct_metadata,
53 Err(error) => {
54 return error.to_compile_error().into();
55 }
56 };
57
58 // Fields are parsed in order: they follow each other without padding (asserted below),
59 // so each field's step consumes exactly the bytes of that field, and the bytes are a
60 // valid value of the struct when every field is a valid value
61 let checks = quote! {
62 let fields = bytes;
63 #(
64 // SAFETY: If `__AB_IO_TYPE_ALIGNED`, `bytes` are aligned for `Self` (function
65 // contract). The field starts after the sizes of the fields before it, which
66 // is its offset in the `#[repr(C)]` layout without padding (asserted below),
67 // a multiple of its alignment, which divides the alignment of `Self`. With
68 // `#[repr(transparent)]` all fields but one are zero-sized with alignment 1,
69 // so that one starts at the beginning. `#[repr(packed)]` is rejected.
70 let (_value, fields) = unsafe {
71 <#field_types as ::ab_io_type::trivial_type::TrivialType>
72 ::parse_step::<__AB_IO_TYPE_ALIGNED>(fields)
73 }?;
74 )*
75 // Empty, there is no padding after the last field
76 let _: &[::core::primitive::u8] = fields;
77 };
78 let (parsing_step, validate_wrapper) =
79 generate_parsing_step(type_name, checks, validate.as_ref());
80 let alignment = generate_alignment(&field_types);
81
82 // A `validate` function rejects some values whose fields are all valid, so such a type
83 // doesn't accept every bit pattern even if its fields do. Bounds are higher-ranked, so
84 // that a field type without the marker makes the bound false instead of being a
85 // "trivial bound" error. They also contain no type parameter, which keeps a future
86 // `try_as_dyn()` probe working.
87 let marker_impl = if validate.is_some() {
88 TokenStream::new()
89 } else {
90 quote! {
91 // SAFETY: Padding is rejected by the assertions generated with the
92 // `TrivialType` impl, so the bytes of a value are exactly the bytes of its
93 // fields, and the impl only applies when every field accepts any bit pattern.
94 // The parsing step only calls parsing steps of fields after checking the
95 // length, which accept every pattern of the field's bytes, and there is no
96 // `validate` function.
97 #[automatically_derived]
98 unsafe impl ::ab_io_type::trivial_type::AnyBitPatternMarker for #type_name
99 where
100 #(
101 for<'__ab_io_type> #field_types:
102 ::ab_io_type::trivial_type::AnyBitPatternMarker,
103 )*
104 {
105 }
106 }
107 };
108
109 quote! {
110 const _: () = {
111 // Assert statically that there is no unexpected padding that would be left
112 // uninitialized and unsound to access
113 ::core::assert!(
114 0 == (
115 ::core::mem::size_of::<#type_name>()
116 #(- ::core::mem::size_of::<#field_types>() )*
117 ),
118 "Struct must not have implicit padding. Consider reordering fields, adding \
119 `padding: [u8; N]` field where necessary or use `Unaligned<T>` wrapper for \
120 types with larger alignment to reduce it to one byte."
121 );
122
123 // Assert that type doesn't exceed 32-bit size limit
124 ::core::assert!(
125 ::core::primitive::u32::MAX as ::core::primitive::usize
126 >= ::core::mem::size_of::<#type_name>(),
127 "Type size must be smaller than 2^32"
128 );
129
130 // Ensure metadata decodes completely, and capacity and alignment are correctly
131 // decoded from it. Every level of nesting takes at least one byte of metadata,
132 // so its length is enough as the max nesting depth.
133 let (type_details, remaining_metadata) =
134 ::ab_io_type::metadata::IoTypeMetadataKind::type_details::<{
135 <#type_name as ::ab_io_type::trivial_type::TrivialType>::METADATA.len()
136 }>(
137 <#type_name as ::ab_io_type::trivial_type::TrivialType>::METADATA,
138 )
139 .expect(
140 "Metadata must decode, which is not the case for layouts with \
141 padding or misaligned fields"
142 );
143 ::core::assert!(remaining_metadata.is_empty());
144 ::core::assert!(
145 ::core::mem::size_of::<#type_name>()
146 == type_details.recommended_capacity as ::core::primitive::usize
147 );
148 // Alignments are compared by their powers, `==` of `Alignment` in constants
149 // needs the `const_trait_impl` and `const_cmp` features
150 ::core::assert!(
151 ::ab_io_type::alignment::Alignment::of::<#type_name>().power
152 == type_details.alignment.power
153 );
154 };
155
156 #[automatically_derived]
157 unsafe impl ::ab_io_type::trivial_type::TrivialType for #type_name
158 where
159 #( #field_types: ::ab_io_type::trivial_type::TrivialType, )*
160 {
161 #alignment
162
163 const METADATA: &[::core::primitive::u8] = #struct_metadata;
164
165 #parsing_step
166 }
167
168 #validate_wrapper
169
170 #marker_impl
171 }
172 }
173 Data::Enum(data_enum) => {
174 // Require defined size of the discriminant instead of allowing compiler to guess. With
175 // `#[repr(u8)]` each variant is laid out like a `#[repr(C)]` struct with the
176 // discriminant as the first field, which padding assertions rely on, `#[repr(C, u8)]`
177 // has a different layout.
178 if let Err(error) = check_repr(
179 &input,
180 &["u8"],
181 "`TrivialType` on enums requires `#[repr(u8)]` without other options",
182 ) {
183 return error.to_compile_error().into();
184 }
185
186 // Metadata identifies variants by their index, so explicit discriminants must be equal
187 // to it, which makes implicit ones equal to it too. Assertions are associated constants
188 // of the enum, so `Self` in a discriminant means the same as in the enum definition.
189 // They are only evaluated when used, so each is used by its own `const _`, which
190 // reports all wrong discriminants.
191 let discriminant_assertions = data_enum
192 .variants
193 .iter()
194 // `#[repr(u8)]` allows at most 256 variants
195 .zip(0..=u8::MAX)
196 .filter_map(|(variant, index)| {
197 let (_eq_token, discriminant) = variant.discriminant.as_ref()?;
198 // Errors point at the discriminant
199 let span = Span::call_site().located_at(discriminant.span());
200 let name = format_ident!("__TRIVIAL_TYPE_DISCRIMINANT_{index}");
201 let message = format!(
202 "Discriminant of `{}` must be equal to its index {index}, variants are \
203 identified by their index in `TrivialType` metadata",
204 variant.ident
205 );
206
207 Some(quote_spanned! {span=>
208 impl #type_name {
209 // The index goes first, so the discriminant is inferred as `u8` like
210 // in the enum definition
211 #[doc(hidden)]
212 const #name: () = ::core::assert!(#index == (#discriminant), #message);
213 }
214
215 const _: () = #type_name::#name;
216 })
217 })
218 .collect::<Vec<_>>();
219
220 let field_types = data_enum
221 .variants
222 .iter()
223 .flat_map(|variant| &variant.fields)
224 .map(|field| &field.ty)
225 .collect::<Vec<_>>();
226
227 let padding_assertions = data_enum.variants.iter().map(|variant| {
228 let field_types = variant.fields.iter().map(|field| &field.ty);
229
230 quote! {
231 // Assert statically that there is no unexpected padding that would be left
232 // uninitialized and unsound to access
233 ::core::assert!(
234 0 == (
235 ::core::mem::size_of::<#type_name>()
236 - ::core::mem::size_of::<::core::primitive::u8>()
237 #(- ::core::mem::size_of::<#field_types>() )*
238 ),
239 "Enum must not have implicit padding. Consider reordering fields, adding \
240 `padding: [u8; N]` field where necessary or use `Unaligned<T>` wrapper for \
241 types with larger alignment to reduce it to one byte."
242 );
243 }
244 });
245
246 // The discriminant must be the index of a variant, which is its discriminant (asserted
247 // above), then fields of that variant are parsed in order. Each variant is laid out
248 // like a `#[repr(C)]` struct with the discriminant as the first field, without padding
249 // (asserted below), so each field's step consumes exactly the bytes of that field, and
250 // the bytes are a valid value of the enum when every field of the variant is a valid
251 // value.
252 let variant_arms = data_enum
253 .variants
254 .iter()
255 // `#[repr(u8)]` allows at most 256 variants
256 .zip(0..=u8::MAX)
257 .map(|(variant, index)| {
258 let field_types = variant.fields.iter().map(|field| &field.ty);
259
260 quote! {
261 #index => {
262 #(
263 // SAFETY: If `__AB_IO_TYPE_ALIGNED`, `bytes` are aligned for `Self`
264 // (function contract). The field starts after the discriminant and
265 // the sizes of the fields before it, which is its offset in the
266 // variant laid out like a `#[repr(C)]` struct without padding
267 // (asserted below), a multiple of its alignment, which divides the
268 // alignment of `Self`.
269 let (_value, fields) = unsafe {
270 <#field_types as ::ab_io_type::trivial_type::TrivialType>
271 ::parse_step::<__AB_IO_TYPE_ALIGNED>(fields)
272 }?;
273 )*
274 // Empty, there is no padding after the last field
275 let _: &[::core::primitive::u8] = fields;
276 }
277 }
278 });
279 let checks = quote! {
280 let (&discriminant, fields) = <[::core::primitive::u8]>::split_first(bytes)?;
281 match discriminant {
282 #( #variant_arms )*
283 _ => {
284 return ::core::option::Option::None;
285 }
286 }
287 };
288 let (parsing_step, validate_wrapper) =
289 generate_parsing_step(type_name, checks, validate.as_ref());
290
291 let alignment = generate_alignment(&field_types);
292
293 let enum_metadata = match generate_enum_metadata(type_name, data_enum) {
294 Ok(struct_metadata) => struct_metadata,
295 Err(error) => {
296 return error.to_compile_error().into();
297 }
298 };
299
300 quote! {
301 #( #discriminant_assertions )*
302
303 const _: () = {
304 #( #padding_assertions )*
305
306 // Assert that type doesn't exceed 32-bit size limit
307 ::core::assert!(
308 ::core::primitive::u32::MAX as ::core::primitive::usize
309 >= ::core::mem::size_of::<#type_name>(),
310 "Type size must be smaller than 2^32"
311 );
312
313 // Ensure metadata decodes completely, and capacity and alignment are correctly
314 // decoded from it. Every level of nesting takes at least one byte of metadata,
315 // so its length is enough as the max nesting depth.
316 let (type_details, remaining_metadata) =
317 ::ab_io_type::metadata::IoTypeMetadataKind::type_details::<{
318 <#type_name as ::ab_io_type::trivial_type::TrivialType>::METADATA.len()
319 }>(
320 <#type_name as ::ab_io_type::trivial_type::TrivialType>::METADATA,
321 )
322 .expect(
323 "Metadata must decode, which is not the case for layouts with \
324 padding or misaligned fields"
325 );
326 ::core::assert!(remaining_metadata.is_empty());
327 ::core::assert!(
328 ::core::mem::size_of::<#type_name>()
329 == type_details.recommended_capacity as ::core::primitive::usize
330 );
331 // Alignments are compared by their powers, `==` of `Alignment` in constants
332 // needs the `const_trait_impl` and `const_cmp` features
333 ::core::assert!(
334 ::ab_io_type::alignment::Alignment::of::<#type_name>().power
335 == type_details.alignment.power
336 );
337 };
338
339 #[automatically_derived]
340 unsafe impl ::ab_io_type::trivial_type::TrivialType for #type_name
341 where
342 #( #field_types: ::ab_io_type::trivial_type::TrivialType, )*
343 {
344 #alignment
345
346 const METADATA: &[::core::primitive::u8] = #enum_metadata;
347
348 #parsing_step
349 }
350
351 #validate_wrapper
352 }
353 }
354 Data::Union(data_union) => {
355 return Error::new(
356 data_union.union_token.span(),
357 "`TrivialType` can be derived for structs and enums, but not unions",
358 )
359 .to_compile_error()
360 .into();
361 }
362 };
363
364 output.into()
365}
366
367/// Generate `TrivialType::ALIGNMENT`: the largest alignment of `field_types`, 1 byte without fields
368fn generate_alignment(field_types: &[&Type]) -> TokenStream {
369 quote! {
370 const ALIGNMENT: ::ab_io_type::alignment::Alignment = {
371 let mut field_alignments: &[::ab_io_type::alignment::Alignment] = &[
372 #( <#field_types as ::ab_io_type::trivial_type::TrivialType>::ALIGNMENT, )*
373 ];
374 let mut alignment =
375 <::core::primitive::u8 as ::ab_io_type::trivial_type::TrivialType>::ALIGNMENT;
376 while let [field_alignment, rest @ ..] = field_alignments {
377 if field_alignment.get() > alignment.get() {
378 alignment = *field_alignment;
379 }
380 field_alignments = rest;
381 }
382 alignment
383 };
384 }
385}
386
387/// Parse `#[trivial_type(..)]` attributes, returns the path of the `validate` function if there is
388/// one
389fn parse_validate(input: &DeriveInput) -> Result<Option<ExprPath>, Error> {
390 // Registering the helper attribute makes it legal on fields and variants too, where the derive
391 // would ignore it, and an ignored `validate` lets parsing create values that `unsafe` code
392 // relies on never existing
393 let nested_attrs = match &input.data {
394 Data::Struct(data_struct) => data_struct
395 .fields
396 .iter()
397 .flat_map(|field| &field.attrs)
398 .collect::<Vec<_>>(),
399 Data::Enum(data_enum) => data_enum
400 .variants
401 .iter()
402 .flat_map(|variant| {
403 variant
404 .attrs
405 .iter()
406 .chain(variant.fields.iter().flat_map(|field| &field.attrs))
407 })
408 .collect::<Vec<_>>(),
409 Data::Union(data_union) => data_union
410 .fields
411 .named
412 .iter()
413 .flat_map(|field| &field.attrs)
414 .collect::<Vec<_>>(),
415 };
416 if let Some(attr) = nested_attrs
417 .into_iter()
418 .find(|attr| attr.path().is_ident("trivial_type"))
419 {
420 return Err(Error::new_spanned(
421 attr,
422 "`#[trivial_type(..)]` is only supported on the type",
423 ));
424 }
425
426 let mut validate = None;
427 let attrs = input
428 .attrs
429 .iter()
430 .filter(|attr| attr.path().is_ident("trivial_type"));
431 for attr in attrs {
432 let mut has_options = false;
433 attr.parse_nested_meta(|meta| {
434 has_options = true;
435 if !meta.path.is_ident("validate") {
436 return Err(meta.error("Unsupported `#[trivial_type(..)]` option"));
437 }
438 if validate.is_some() {
439 return Err(meta.error("`validate` is specified more than once"));
440 }
441 // Parsed like a path in an expression, so generic arguments are written as `::<..>`
442 // and a qualified self type is supported, like in the generated call
443 let path = meta.value()?.parse::<ExprPath>()?;
444 if let Some(attr) = path.attrs.first() {
445 return Err(Error::new_spanned(
446 attr,
447 "Attributes are not supported in `validate = path`",
448 ));
449 }
450 validate = Some(path);
451 Ok(())
452 })?;
453
454 if !has_options {
455 return Err(Error::new_spanned(
456 attr,
457 "`#[trivial_type(..)]` requires an option, like `validate = path`",
458 ));
459 }
460 }
461
462 Ok(validate)
463}
464
465/// Generate the parsing step of `TrivialType`, which checks the size, runs `checks` with `bytes` of
466/// the size of the type in scope, which must return `None` for invalid bytes, casts `bytes` to the
467/// type and calls the `validate` function, if there is one, with a reference to the value: into
468/// `bytes` in the aligned flavour, to a copy in the unaligned flavour.
469///
470/// Also returns an inherent impl of the type with a safe wrapper of the `validate` function, which
471/// the step calls, empty if there is no `validate` function.
472fn generate_parsing_step(
473 type_name: &Ident,
474 checks: TokenStream,
475 validate: Option<&ExprPath>,
476) -> (TokenStream, TokenStream) {
477 let (validation, validate_wrapper) = if let Some(path) = validate {
478 // An `unsafe` function called directly in the `unsafe` step would compile: its body is an
479 // unsafe context before edition 2024, and since then such a call only triggers a lint,
480 // which isn't reported in derive output. So it is called from a safe wrapper instead.
481 // Errors, like a wrong signature or an `unsafe` function, point at the path in the
482 // attribute. The parameter has a prefixed name: it would shadow a `validate` function with
483 // the same name, and a constant with the same name in the module of the type would turn
484 // it into a constant pattern.
485 let span = Span::call_site().located_at(path.span());
486 let validate_wrapper = quote_spanned! {span=>
487 impl #type_name {
488 #[doc(hidden)]
489 #[inline(always)]
490 fn __ab_io_type_validate(__ab_io_type_value: &Self) -> ::core::primitive::bool {
491 #path(__ab_io_type_value)
492 }
493 }
494 };
495 // The wrapper is called in each branch: the copy of the unaligned flavour is a temporary,
496 // which is dropped at the end of its branch
497 let validation = quote! {
498 if !(if __AB_IO_TYPE_ALIGNED {
499 // SAFETY: `bytes` are aligned for `Self` (function contract) and are a valid value
500 // of it, as checked above
501 Self::__ab_io_type_validate(unsafe {
502 &*<[::core::primitive::u8]>::as_ptr(bytes).cast::<Self>()
503 })
504 } else {
505 // `bytes` may not be aligned for `Self`, so the value is copied
506 Self::__ab_io_type_validate(
507 &::ab_io_type::unaligned::Unaligned::<Self>::as_inner(__ab_io_type_value),
508 )
509 }) {
510 return ::core::option::Option::None;
511 }
512 };
513 (validation, validate_wrapper)
514 } else {
515 (TokenStream::new(), TokenStream::new())
516 };
517
518 // The value is the only local of the step with a prefixed name: a constant with the same name
519 // in the module of the type would turn the binding into a constant pattern, which compiles for
520 // types with a single value, and the step would return the constant instead of a reference
521 // into `bytes`. Constants named like the other locals only cause compile errors. The generic
522 // parameter is prefixed too, since a constant with the same name in a field type, like the
523 // length of an array, would refer to the parameter instead.
524 let parsing_step = quote! {
525 #[inline(always)]
526 unsafe fn parse_step<const __AB_IO_TYPE_ALIGNED: ::core::primitive::bool>(
527 bytes: &[::core::primitive::u8],
528 ) -> ::core::option::Option<(
529 &::ab_io_type::unaligned::Unaligned<Self>,
530 &[::core::primitive::u8],
531 )> {
532 let (bytes, remainder) = <[::core::primitive::u8]>::split_at_checked(
533 bytes,
534 ::core::mem::size_of::<Self>(),
535 )?;
536 #checks
537 // SAFETY: `bytes` have the size of the type and `checks` return `None` unless every
538 // field, of the variant whose index is the discriminant for an enum, is a valid value.
539 // Variants have discriminants equal to their indices and fields follow each other
540 // without padding (both asserted next to the type), and `Unaligned<Self>` has
541 // alignment 1.
542 let __ab_io_type_value = unsafe {
543 &*<[::core::primitive::u8]>::as_ptr(bytes)
544 .cast::<::ab_io_type::unaligned::Unaligned<Self>>()
545 };
546 // The `validate` function, if any, rejects values whose invariants don't hold
547 #validation
548 ::core::option::Option::Some((__ab_io_type_value, remainder))
549 }
550 };
551
552 (parsing_step, validate_wrapper)
553}
554
555/// Check that `#[repr(..)]` attributes of the type contain at least one of `allowed` options and no
556/// other options, returns an error with `message` otherwise
557fn check_repr(input: &DeriveInput, allowed: &[&str], message: &str) -> Result<(), Error> {
558 let mut found_allowed = false;
559
560 // All `#[repr(..)]` attributes are combined by the compiler
561 let repr_attrs = input
562 .attrs
563 .iter()
564 .filter(|attr| attr.path().is_ident("repr"));
565 for repr_attr in repr_attrs {
566 repr_attr.parse_nested_meta(|meta| {
567 if allowed.iter().any(|option| meta.path.is_ident(option)) {
568 found_allowed = true;
569 Ok(())
570 } else {
571 Err(meta.error(message))
572 }
573 })?;
574 }
575
576 if !found_allowed {
577 return Err(Error::new(input.ident.span(), message));
578 }
579
580 Ok(())
581}
582
583fn generate_struct_metadata(ident: &Ident, data_struct: &DataStruct) -> Result<TokenStream, Error> {
584 let inner_struct_metadata = generate_inner_struct_metadata(ident, &data_struct.fields)
585 .collect::<Result<Vec<_>, _>>()?;
586
587 // Encodes the following:
588 // * Type: struct
589 // * The rest as inner struct metadata
590 Ok(quote! {{
591 #[inline(always)]
592 const fn metadata() -> (
593 [::core::primitive::u8; ::ab_io_type::metadata::MAX_METADATA_CAPACITY],
594 ::core::primitive::usize,
595 )
596 {
597 ::ab_io_type::metadata::concat_metadata_sources(&[
598 &[::ab_io_type::metadata::IoTypeMetadataKind::Struct as ::core::primitive::u8],
599 #( #inner_struct_metadata )*
600 ])
601 }
602
603 // The reference extends the lifetime of metadata scratch to that of the constant
604 let (metadata_scratch, metadata_size) = &metadata();
605 // `split_at()` is called by path, with method call syntax a method of a trait in scope that
606 // is implemented for arrays would be called instead
607 <[_]>::split_at(metadata_scratch, *metadata_size).0
608 }})
609}
610
611fn generate_enum_metadata(ident: &Ident, data_enum: &DataEnum) -> Result<TokenStream, Error> {
612 let type_name_string = ident.to_string();
613 let type_name_bytes = type_name_string.as_bytes();
614
615 let type_name_bytes_len = u8::try_from(type_name_bytes.len()).map_err(|_error| {
616 Error::new(
617 ident.span(),
618 format!(
619 "Name of the enum must not be more than {} bytes in length",
620 u8::MAX
621 ),
622 )
623 })?;
624 let num_variants = u8::try_from(data_enum.variants.len()).map_err(|_error| {
625 Error::new(
626 ident.span(),
627 format!("Enum must not have more than {} variants", u8::MAX),
628 )
629 })?;
630 // Encodes the following:
631 // * Type: enum
632 // * Length of enum name in bytes (u8)
633 // * Enum name as UTF-8 bytes
634 // * Number of variants (u8)
635 let enum_metadata_header = {
636 let enum_metadata_header = [Literal::u8_unsuffixed(type_name_bytes_len)]
637 .into_iter()
638 .chain(
639 type_name_bytes
640 .iter()
641 .map(|&char| Literal::byte_character(char)),
642 )
643 .chain([Literal::u8_unsuffixed(num_variants)]);
644
645 quote! {
646 &[
647 ::ab_io_type::metadata::IoTypeMetadataKind::Enum as ::core::primitive::u8,
648 #( #enum_metadata_header, )*
649 ]
650 }
651 };
652
653 // Encodes each variant as inner struct, fields of tuple variants have zero-length names like
654 // fields of tuple structs
655 let inner = data_enum
656 .variants
657 .iter()
658 .flat_map(|variant| generate_inner_struct_metadata(&variant.ident, &variant.fields))
659 .collect::<Result<Vec<TokenStream>, Error>>()?;
660
661 Ok(quote! {{
662 #[inline(always)]
663 const fn metadata() -> (
664 [::core::primitive::u8; ::ab_io_type::metadata::MAX_METADATA_CAPACITY],
665 ::core::primitive::usize,
666 )
667 {
668 ::ab_io_type::metadata::concat_metadata_sources(&[
669 #enum_metadata_header,
670 #( #inner )*
671 ])
672 }
673
674 // The reference extends the lifetime of metadata scratch to that of the constant
675 let (metadata_scratch, metadata_size) = &metadata();
676 // `split_at()` is called by path, with method call syntax a method of a trait in scope that
677 // is implemented for arrays would be called instead
678 <[_]>::split_at(metadata_scratch, *metadata_size).0
679 }})
680}
681
682fn generate_inner_struct_metadata<'a>(
683 ident: &'a Ident,
684 fields: &'a Fields,
685) -> impl Iterator<Item = Result<TokenStream, Error>> + 'a {
686 iter::once_with(move || generate_inner_struct_metadata_header(ident, fields))
687 .chain(generate_fields_metadata(fields))
688}
689
690fn generate_inner_struct_metadata_header(
691 ident: &Ident,
692 fields: &Fields,
693) -> Result<TokenStream, Error> {
694 let ident_string = ident.to_string();
695 let ident_bytes = ident_string.as_bytes();
696
697 let ident_bytes_len = u8::try_from(ident_bytes.len()).map_err(|_error| {
698 Error::new(
699 ident.span(),
700 format!(
701 "Identifier must not be more than {} bytes in length",
702 u8::MAX
703 ),
704 )
705 })?;
706 let num_fields = u8::try_from(fields.len()).map_err(|_error| {
707 Error::new(
708 fields.span(),
709 format!("Must not have more than {} fields", u8::MAX),
710 )
711 })?;
712
713 // Encodes the following:
714 // * Length of identifier in bytes (u8)
715 // * Identifier as UTF-8 bytes
716 // * Number of fields (u8)
717 Ok({
718 let struct_metadata_header = [Literal::u8_unsuffixed(ident_bytes_len)]
719 .into_iter()
720 .chain(
721 ident_bytes
722 .iter()
723 .map(|&char| Literal::byte_character(char)),
724 )
725 .chain([Literal::u8_unsuffixed(num_fields)]);
726
727 quote! {
728 &[#( #struct_metadata_header, )*],
729 }
730 })
731}
732
733fn generate_fields_metadata(
734 fields: &Fields,
735) -> impl Iterator<Item = Result<TokenStream, Error>> + '_ {
736 // Encodes the following for each field:
737 // * Length of the field name in bytes (u8, zero for tuple fields)
738 // * Field name as UTF-8 bytes
739 // * Recursive metadata of the field's type
740 fields.iter().map(move |field| {
741 let field_metadata = if let Some(field_name) = &field.ident {
742 let field_name_string = field_name.to_string();
743 let field_name_bytes = field_name_string.as_bytes();
744 let field_name_len = u8::try_from(field_name_bytes.len()).map_err(|_error| {
745 Error::new(
746 field.span(),
747 format!(
748 "Name of the field must not be more than {} bytes in length",
749 u8::MAX
750 ),
751 )
752 })?;
753
754 let field_metadata = [Literal::u8_unsuffixed(field_name_len)].into_iter().chain(
755 field_name_bytes
756 .iter()
757 .map(|&char| Literal::byte_character(char)),
758 );
759
760 quote! { #( #field_metadata, )* }
761 } else {
762 quote! { 0, }
763 };
764 let field_type = &field.ty;
765
766 Ok(quote! {
767 &[ #field_metadata ],
768 <#field_type as ::ab_io_type::trivial_type::TrivialType>::METADATA,
769 })
770 })
771}