macro_rules! def_attrs {
    (@attr_ty str) => { ... };
    (@attr_ty bool) => { ... };
    (@attr_ty [str]) => { ... };
    (@attr_ty none) => { ... };
    (@attr_ty {
        $(#[$m:meta])*
        $vis:vis $name:ident($what:literal) {
            $($attr_name:ident $kind:tt),+
        }
    }) => { ... };
    (@match_attr_with $attr_name:ident, $meta:ident, $self:ident, $matched:expr) => { ... };
    (@match_attr str $attr_name:ident, $meta:ident, $self:ident) => { ... };
    (@match_attr bool $attr_name:ident, $meta:ident, $self:ident) => { ... };
    (@match_attr [str] $attr_name:ident, $meta:ident, $self:ident) => { ... };
    (@match_attr none $attr_name:ident, $meta:ident, $self:ident) => { ... };
    (@match_attr {
        $(#[$m:meta])*
        $vis:vis $name:ident($what:literal) $body:tt
    } $attr_name:ident, $meta:expr, $self:ident) => { ... };
    (@def_ty $list_name:ident str) => { ... };
    (@def_ty $list_name:ident bool) => { ... };
    (@def_ty $list_name:ident [str]) => { ... };
    (@def_ty $list_name:ident none) => { ... };
    (
        @def_ty $list_name:ident {
            $(#[$m:meta])*
            $vis:vis $name:ident($what:literal) {
                $($attr_name:ident $kind:tt),+
            }
        }
    ) => { ... };
    (
        @def_struct
        $list_name:ident
        $(#[$m:meta])*
        $vis:vis $name:ident($what:literal) {
            $($attr_name:ident $kind:tt),+
        }
    ) => { ... };
    (
        crate $list_name:ident;
        $(
            $(#[$m:meta])*
            $vis:vis $name:ident($what:literal) {
                $($attr_name:ident $kind:tt),+
            }
        );+;
    ) => { ... };
}
Expand description

Generates one or more structures used for parsing attributes in proc macros.

Generated structures have one static method called parse that accepts a slice of Attributes. The method finds attributes that contain meta lists (look like #[your_custom_ident(...)]) and fills a newly allocated structure with values of the attributes if any.

The expected input looks as follows:

def_attrs! {
    crate zvariant;

    /// A comment.
    pub StructAttributes("struct") { foo str, bar str, baz none };
    #[derive(Hash)]
    FieldAttributes("field") { field_attr bool };
}

Here we see multiple entries: an entry for an attributes group called StructAttributes and another one for FieldAttributes. The former has three defined attributes: foo, bar and baz. The generated structures will look like this in that case:

/// A comment.
#[derive(Default, Clone, Debug)]
pub struct StructAttributes {
    foo: Option<String>,
    bar: Option<String>,
    baz: bool,
}

#[derive(Hash)]
#[derive(Default, Clone, Debug)]
struct FieldAttributes {
    field_attr: Option<bool>,
}

foo and bar attributes got translated to fields with Option<String> type which contain the value of the attribute when one is specified. They are marked with str keyword which stands for string literals. The baz attribute, on the other hand, has bool type because it’s an attribute without value marked by the none keyword.

Currently the following literals are supported:

  • str - string literals;
  • bool - boolean literals;
  • [str] - lists of string literals (#[macro_name(foo("bar", "baz"))]);
  • none - no literal at all, the attribute is specified alone.

The strings between braces are embedded into error messages produced when an attribute defined for one attribute group is used on another group where it is not defined. For example, if the field_attr attribute was encountered by the generated StructAttributes::parse method, the error message would say that it “is not allowed on structs”.

Nested attribute lists

It is possible to create nested lists for specific attributes. This is done as follows:

def_attrs! {
    crate zvariant;

    pub OuterAttributes("outer") {
        simple_attr bool,
        nested_attr {
            /// An example of nested attributes.
            pub InnerAttributes("inner") {
                inner_attr str
            }
        }
    };
}

The syntax for inner attributes is the same as for the outer attributes, but you can specify only one inner attribute per outer attribute.

Calling the macro multiple times

The macro generates an array called ALLOWED_ATTRS that contains a list of allowed attributes. Calling the macro twice in the same scope will cause a name alias and thus will fail to compile. You need to place each macro invocation into a module in that case.

Errors

The generated parse method checks for some error conditions:

  1. Unknown attributes. When multiple attribute groups are defined in the same macro invocation, one gets a different error message when providing an attribute from a different attribute group.
  2. Duplicate attributes.
  3. Missing attribute value or present attribute value when none is expected.
  4. Invalid literal type for attributes with values.