struct BuildInput {
package_name: LitStr,
commands: Vec<Ident>,
}
impl Parse for BuildInput {
fn parse(input: ParseStream) -> syn::Result<Self> {
let package_name: LitStr = input.parse()?;
let _: Token![,] = input.parse()?;
let mut commands = Vec::new();
loop {
if input.is_empty() {
break;
}
let name: Ident = input.parse()?;
commands.push(name);
if input.parse::<Token![,]>().is_err() {
break;
}
}
if commands.is_empty() {
return Err(syn::Error::new(
package_name.span(),
"build! requires at least one command function after the package name",
));
}
Ok(BuildInput {
package_name,
commands,
})
}
}
#[proc_macro]
pub fn build(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as BuildInput);
let package_name = &input.package_name;
let chain: TokenStream2 = input
.commands
.iter()
.map(|fn_name| {
let meta_ident = Ident::new(
&format!("__RUstra_meta_{}", fn_name),
proc_macro2::Span::call_site(),
);
let cap_ident = Ident::new(
&format!("__RUstra_cap_{}", fn_name),
proc_macro2::Span::call_site(),
);
let platforms_ident = Ident::new(
&format!("__RUstra_platforms_{}", fn_name),
proc_macro2::Span::call_site(),
);
let errors_ident = Ident::new(
&format!("__RUstra_errors_{}", fn_name),
proc_macro2::Span::call_site(),
);
let devices_ident = Ident::new(
&format!("__RUstra_devices_{}", fn_name),
proc_macro2::Span::call_site(),
);
let doc_ident = Ident::new(
&format!("__RUstra_doc_{}", fn_name),
proc_macro2::Span::call_site(),
);
let register_ident = Ident::new(
&format!("__rustra_register_{}", fn_name),
proc_macro2::Span::call_site(),
);
quote! {
.command(#meta_ident, #register_ident)
.command_doc(#meta_ident, #doc_ident)
.require_capability_if(#meta_ident, #cap_ident)
.platform_meta_if(#meta_ident, #platforms_ident)
.errors_meta_if(#meta_ident, #errors_ident)
.devices_meta_if(#meta_ident, #devices_ident)
}
})
.collect();
let expanded = quote! {
rustra::Package::builder(#package_name) #chain
};
expanded.into()
}