The whole 'Foo' thing isn't very clear since it's abstract and meaningless names, but I imagine from the earlier exchanges that you want your custom derive macro FooBlah
to simply instantiate two attributes in front of its content, struct WantsFoo
:
#[derive(crate1::Foo)]
#[foo_param(blah)]
A derive macro is neutral regarding the attached item: it will only add items after it. So I believe you need an attribute macro, which is able to modify its content. In this case, you want to add attributes in front of it, that you will likely specify in argument, like a list of the attributes you want:
#[proc_macro_attribute]
pub fn multi_attr(args: TokenStream, input: TokenStream) -> TokenStream {
macro_multi_attr(args.into(), input.into()).into()
}
where macro_multi_attr(args: proc_macro2::TokenStream, input: proc_macro2::TokenStream)
. These proc_macro2
types are just wrappers around the proc_macro::TokenStream
of the top-level macro, and the reason I mention this is because they're heavily used in the traits and macros provided in the syn
create, which you definitely want to use. And having a separate function using only the proc_macro2
type will give you much more flexibility (if you want to put that in another non-proc crate, do unit testing, etc). The .into()
converts one type into the other.
In that function, you need to
- parse the arguments from
args
- adds the appropriate attributes to
item
to create the output TokenStream
The typical way to go about it would be to store the data required to generate the attributes into an object, let's say a struct MyArgs
, and implement Parse
and VisitMut
for that object.
The Parse
trait allows you to parse the argument stream, args
, and extract the data you need (a list of attribute names, possibly their types, etc):
impl Parse for MyArgs {
fn parse(args: ParseStream) -> syn::parse::Result<Self> {
/* parsing your format, likely a comma-separated list,
so using something like
Punctuated::<Ident, Token![,]>::parse_terminated(args)?... */
Ok(MyArgs{ /* ... */ })
}
}
The VisitMut
trait allows you to modify items without having to parse and re-create everything. This one is used to process the actual content, input
. The top item in the TokenStream
will be syn::File
, to which you can add attributes, so you could modify only that, and your code would look like this:
impl VisitMut for MyArgs {
fn visit_file_mut(&mut self, file: &mut File) {
let attrs: Vec<syn::Attribute> = /* ... */;
file.attrs.extend(attrs);
}
}
You can create each attribute by using quote!
, for example.