struct RegisterInput {
builder: syn::Expr,
commands: Vec<Ident>,
}
impl Parse for RegisterInput {
fn parse(input: ParseStream) -> syn::Result<Self> {
let builder: syn::Expr = input.parse()?;
let _: Token![,] = input.parse()?;
let mut commands = Vec::new();
loop {
let name: Ident = input.parse()?;
commands.push(name);
if input.parse::<Token![,]>().is_err() {
break;
}
}
Ok(RegisterInput { builder, commands })
}
}
fn command_chain_entry(fn_name: &Ident) -> TokenStream2 {
let meta_ident = Ident::new(
&format!("__RUstra_meta_{}", fn_name),
proc_macro2::Span::call_site(),
);
let execution_ident = Ident::new(
&format!("__RUstra_execution_{}", 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)
.command_execution(#meta_ident, #execution_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)
}
}
#[proc_macro]
pub fn register(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as RegisterInput);
if input.commands.is_empty() {
return syn::Error::new(
proc_macro2::Span::call_site(),
"register! requires at least one command function after the builder expression",
)
.to_compile_error()
.into();
}
let builder = &input.builder;
let chain: TokenStream2 = input.commands.iter().map(command_chain_entry).collect();
let expanded = quote! {
#builder #chain
};
expanded.into()
}