macro_rules! format_struct {
    ($($(#[$m:meta])* $vis:vis struct $endian:tt $name:ident {$($field_name:ident: $ty:tt),*,})+) => { ... };
    (@store_type [u8; $sz:literal]) => { ... };
    (@store_type $ty:ty) => { ... };
    (@getter $endian:tt $field_name:ident [u8; $sz:literal]) => { ... };
    (@getter little $field_name:ident $ty:ty) => { ... };
    (@getter big $field_name:ident $ty:ty) => { ... };
    (@setter_or_mut $endian:tt $field_name:ident [u8; $sz:literal]) => { ... };
    (@setter_or_mut little $field_name:ident $ty:ty) => { ... };
    (@setter_or_mut big $field_name:ident $ty:ty) => { ... };
    (@debug_target $self:ident.$field_name:ident [u8; $sz:literal]) => { ... };
    (@debug_target $self:ident.$field_name:ident $ty:ty) => { ... };
}
Expand description

Defines a structure that can be transmuted from/into a byte slice for parsing/constructing binary formats in a zero-copy way. That works due to the generated structure having alignment of 1 byte and no internal padding. Generated structures will have the FromByteSlice trait implemented on them.

The macro accepts syntax similar to a standard structure definition in Rust with some differences:

  • The struct keyword is followed by either little or big keywords that specify which endianness to use for the structure’s integer fields.
  • Fields of the generated structure may not have any meta attached to them which includes the docs. This may be changed in the future.

Examples

format_struct! {
    /// A little-endian test structure.
    #[derive(Default, Clone)]
    pub struct little Test {
        byte: u8,
        short: u16,
        word: i32,
        dword: i64,
        qword: u128,
        byte_arr: [u8; 16],
    }
}

It is also possible to define multiple structures in one macro invocation:

format_struct! {
    struct little Foo {
        byte: u8,
    }

    struct big Bar {
        a: u64,
    }

    pub struct little Baz {
        z: [u8; 33],
    }
}

Allowed field types

Currently only integer types (u8, u16, u32, u64, u128 and their signed counterparts) are allowed and statically sized byte arrays ([u8; N]).

Layout

The fields in the structure are laid out in declaration order without any padding. That means that the following structure will take 7 bytes instead of 16 you might expect:

format_struct! {
    struct little SmallStruct {
        byte: u8,
        dword: u64,
    }
}

Inner workings

Structures generated by this macro will have their integer fields replaced with appropriately sized byte arrays. Integer fields will be exposed by generated getters and setters which use from_(le|be)_bytes and to_(le|be)_bytes to convert between the integer types and byte arrays. Byte array fields will be stored as is and exposed using references.