on
Procedural Macros, pt. 3: Code
The synstructure
crate provides a good starting point for our derive macro via
decl_derive.
Let’s break down the code snippet above
decl_derive!(...);
is a macro in thesynstructure
crate.[propertese, ....] => ...
declares the name of the derive macro we are writing. In this case,propertese
.[..., attributes(propertese)]
allows us to pass in attributes to our derive macro.[...] => #[proc_macro_error] derive_propertese
specifies thatderive_propertese
is the function that will be called to generate the macro’s code. This function has to take in asynstructure::Strutcture
as an input parameter and return aTokenStream
.#[proc_macro_error]
helps us to better manage errors generated by our procedural macro.
This essentially allows us to do something like the following, and have our getters automatically generated.
Let’s take an initial stab at filling in our function derive_propertese
.
Break it down again
While it looks a bit, the snippet above can be broken down neatly
let mut propertese_token_stream = proc_macro2::TokenStream::new();
Our
derive_propertese
function is to return aTokenStream
. This just declares a mutable one. As we build the token streams for each field’s getter, we will append to it.match &s.ast().data ...
synstructure::Structure
gives us an AST (Abstract Syntax Tree) to work on. The data property on the AST can refer to a Struct, Enum or Union as the case may be. We are only interested in data that has a type of Struct, particularly, ones with named fields. In other instances, we panic as our macro does not support it.fields_named.named_iter().for_each(...)
We iterate over each field and build a getter for it. At a later point, we will add functionality that allows us to skip its generation entirely.
Stay calm. Do not panic
In the above snippet, our code generates a panic
anytime it encounters an
error. To handle these better, let’s convert these panic
s into errors. We do
so with the help of the macro emit_error
(from the crate proc_macro_error
),
which takes in an error message and a
span for the
error.
We have a good base here. Let’s follow it up with writing the code to generate our getters in the next part to this series.