I'm trying to write a Rust macro that allows me to make use of the field names and types of a struct declaration, but I still need to emit the struct.
I've got it working with optional attributes, visibility of the struct (thanks to The Little Book of Rust Macros), but can't figure out how to deal with the optional presence of pub
in the individual fields.
So far I've got:
macro_rules! with_generic {
($(#[$struct_meta:meta])*
pub struct $name:ident { $($fname:ident : $ftype:ty), *}
) => {
with_generic![(pub) $(#[$struct_meta])* struct $name {$($fname: $ftype) ,*}];
};
($(#[$struct_meta:meta])*
struct $name:ident { $($fname:ident : $ftype:ty), *}
) => {
with_generic![() $(#[$struct_meta])* struct $name {$($fname: $ftype), *}];
};
(
($($vis:tt)*)
$(#[$struct_meta:meta])*
struct $name:ident { $($fname:ident : $ftype:ty), *}
) => {
// emit the struct here
$(#[$struct_meta])*
$($vis)* struct $name {
$($fname: $ftype,)*
}
// I work with fname and ftypes here
}
}
And it works with something like
with_generic! {
#[derive(PartialEq, Eq, Debug)]
pub struct Person {
first_name: String,
last_name: String
}
}
or
with_generic! {
#[derive(PartialEq, Eq, Debug)]
struct PrivatePerson {
first_name: String,
last_name: String
}
}
but doesn't work with
with_generic! {
#[derive(PartialEq, Eq, Debug)]
struct MixedPerson {
pub first_name: String,
last_name: String
}
}
I'd like to get some help on how to make the macro work with that last case. I feel like I might be missing something basic here, such as the type used for binding visibility. If there's a way to bind the whole struct tree while getting the field names and types, that would also be fine.
I'd also like to learn how to get it to work with structs that have lifetime parameters, but maybe that should be a separate question.
You can't. At least, not with a single, non-recursive rule. This is because Rust doesn't have a macro matcher for visibility.
The parse-macros
crate contains a parse_struct!
macro that shows the work necessary to completely parse a struct
definition. Short version: you need to parse each field individually, with one rule for each of "has pub
" and "doesn't have pub
".
I'd also just note that there's another case your macro doesn't yet account for: attributes on the fields, which is needed for doc-comments on them to work.
Quite soon, macros 1.1 should be stabilised, which might provide an easier approach (assuming you can express your macro as a derivation, and don't care about older versions of Rust).