Search code examples
jsonserializationrustmacrosmetaprogramming

Rust macro with optional attribute statements over fields


I am trying to make a macro that modifies the declaration of a struct, for now I have this:

macro_rules! Reflect
{
(pub struct $name:ident {$($field_name:ident : $field_type:ty,) *}) =>
{
    #[derive(Debug, Clone, serde::Serialize)]
    #[serde(rename_all = "camelCase")]
    pub struct $name
    {
        $(pub $field_name : $field_type,) *
    }
}

Reflect!(
pub struct Node
{
    name : String,
    #[serde(skip_serializing_if = "<[_]>::is_empty")] // causes compilation error because macro does not expect it
    children : Vec::<usize>,
    rotation : na::UnitQuaternion::<f32>,
    translation : na::Vector3::<f32>,
    scale : na::Vector3::<f32>,
    skin : usize,
    mesh : usize,
}
);

In this case I would like the serde attribbute declaration to be left intact on top of the field if defined.

With the above I get a compilation error because the macro does not have the right pattern to handle it, but I don't know how to do it.


Solution

  • You can capture attributes with the :meta fragment:

    macro_rules! Reflect
    {
        (
            pub struct $name:ident {
                $(
                    $( #[$attrs:meta] )*
                    $field_name:ident : $field_type:ty,
                )*
            }
        ) =>
        {
            #[derive(Debug, Clone, serde::Serialize)]
            #[serde(rename_all = "camelCase")]
            pub struct $name
            {
                $(
                    $( #[$attrs] )*
                    pub $field_name : $field_type,
                )*
            }
        };
    }