Skip to main content

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}