Skip to main content

custom_struct

Macro custom_struct 

Source
macro_rules! custom_struct {
    (
        attrs: { $($attrs:tt)* }
        $(#$attr:tt)*
        $vis:vis struct $name:ident { $($body:tt)* }
    ) => { ... };
    (
        @split_attrs,
        cfg_attrs: { $($cfg_attrs:tt)* },
        other_attrs: { $($other_attrs:tt)* },
        remaining_attrs: {
            #[cfg($($cfg:tt)*)]
            $($tail:tt)*
        },
        found_exhaustive_attr: $found_exhaustive:tt,
        vis: $vis:vis,
        name: $name:ident,
        body: { $($body:tt)* },
    ) => { ... };
    (
        @split_attrs,
        cfg_attrs: { $($cfg_attrs:tt)* },
        other_attrs: { $($other_attrs:tt)* },
        remaining_attrs: {
            #[exhaustive]
            $($tail:tt)*
        },
        found_exhaustive_attr: $_:tt,
        vis: $vis:vis,
        name: $name:ident,
        body: { $($body:tt)* },
    ) => { ... };
    (
        @split_attrs,
        cfg_attrs: { $($cfg_attrs:tt)* },
        other_attrs: { $($other_attrs:tt)* },
        remaining_attrs: {
            #$other:tt
            $($tail:tt)*
        },
        found_exhaustive_attr: $found_exhaustive:tt,
        vis: $vis:vis,
        name: $name:ident,
        body: { $($body:tt)* },
    ) => { ... };
    (
        @split_attrs,
        cfg_attrs: { $($cfg_attrs:tt)* },
        other_attrs: { $($other_attrs:tt)* },
        remaining_attrs: { },
        found_exhaustive_attr: $found_exhaustive:tt,
        vis: $vis:vis,
        name: $name:ident,
        body: { $($body:tt)* },
    ) => { ... };
    (
        @struct,
        cfg_attrs: { $($cfg_attrs:tt)* },
        other_attrs: { $($other_attrs:tt)* },
        found_exhaustive_attr: $found_exhaustive:tt,
        vis: $vis:vis,
        name: $name:ident,
        processed_fields: { $($processed_fields:tt)* },
        processed_field_defaults: { $($processed_field_defaults:tt)* },
        remaining_fields: {
            #[custom_default($default:expr)]
            $(#[$fattr:meta])*
            $fvis:vis $fname:ident: $fty:ty,
            $($tail:tt)*
        },
    ) => { ... };
    (
        @struct,
        cfg_attrs: { $($cfg_attrs:tt)* },
        other_attrs: { $($other_attrs:tt)* },
        found_exhaustive_attr: $found_exhaustive:tt,
        vis: $vis:vis,
        name: $name:ident,
        processed_fields: { $($processed_fields:tt)* },
        processed_field_defaults: { $($processed_field_defaults:tt)* },
        remaining_fields: {
            $(#[$fattr:meta])*
            $fvis:vis $fname:ident: $fty:ty,
            $($tail:tt)*
        },
    ) => { ... };
    (
        @struct,
        cfg_attrs: { $($cfg_attrs:tt)* },
        other_attrs: { $($other_attrs:tt)* },
        found_exhaustive_attr: $found_exhaustive:tt,
        vis: $vis:vis,
        name: $name:ident,
        processed_fields: { $($processed_fields:tt)* },
        processed_field_defaults: { $($processed_field_defaults:tt)* },
        remaining_fields: { },
    ) => { ... };
}
Expand description

Emit a struct with the given derive attributes plus a generated Default impl. Ensure that the record has an additional private field added to replicate #[non_exhaustive], unless it is annotated with #[exhaustive].

Fields default to Default::default(). A field whose default can’t be derived must carry #[custom_default(EXPR)] as its first attribute, and EXPR is used instead.

This works by scanning each field for #[custom_default] attributes. If one exists, the attribute’s contents are added to processed_field_defaults and will be used in the expansion for Default. If it does not exist, Default::default() is used instead. In either case, the field is added to processed_fields with #[custom_default] stripped if necessary, and custom_struct is invoked again with the remaining fields.

Attributes are split into cfg_attrs and other_attrs before the fields are scanned. Both go on the struct, but only the cfgs are repeated on the Default impl. A cfg decides whether the type exists at all, so without it a configured-out struct leaves an impl behind referring to a type that isn’t there.

Out of other_attrs, we scan for #[exhaustive]. If found, we remove it but take into account that the record should be expanded without an additional private field. The scan for both cfg attributes and the (non-existent) exhaustive attribute is done in one linear pass.