use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Error};
#[proc_macro_derive(AutoRegisterTask)]
pub fn auto_register_task(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
impl_auto_register_task(&input).unwrap_or_else(|err| err.to_compile_error().into())
}
fn impl_auto_register_task(input: &DeriveInput) -> Result<TokenStream, Error> {
let type_name = &input.ident;
let expanded = quote! {
::rust_task_queue::inventory::submit! {
::rust_task_queue::TaskRegistration {
type_name: stringify!(#type_name),
register_fn: |registry: &::rust_task_queue::TaskRegistry| -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let temp_instance = <#type_name as Default>::default();
let task_name = temp_instance.name();
registry.register_with_name::<#type_name>(task_name)
},
}
}
};
Ok(expanded.into())
}
#[proc_macro_attribute]
pub fn register_task(args: TokenStream, input: TokenStream) -> TokenStream {
let task_name =
if args.is_empty() {
None
} else {
match syn::parse::<syn::LitStr>(args.clone()) {
Ok(lit) => Some(lit.value()),
Err(_) => return Error::new_spanned(
proc_macro2::TokenStream::from(args),
"Expected string literal for task name, e.g., #[register_task(\"my_task_name\")]"
).to_compile_error().into(),
}
};
let input = parse_macro_input!(input as DeriveInput);
impl_register_task_with_name(&input, task_name)
.unwrap_or_else(|err| err.to_compile_error().into())
}
fn impl_register_task_with_name(
input: &DeriveInput,
custom_name: Option<String>,
) -> Result<TokenStream, Error> {
let type_name = &input.ident;
let registration_code = if let Some(name) = custom_name {
quote! {
::rust_task_queue::inventory::submit! {
::rust_task_queue::TaskRegistration {
type_name: stringify!(#type_name),
register_fn: |registry: &::rust_task_queue::TaskRegistry| -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
registry.register_with_name::<#type_name>(#name)
},
}
}
}
} else {
quote! {
::rust_task_queue::inventory::submit! {
::rust_task_queue::TaskRegistration {
type_name: stringify!(#type_name),
register_fn: |registry: &::rust_task_queue::TaskRegistry| -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let temp_instance = <#type_name as Default>::default();
let task_name = temp_instance.name();
registry.register_with_name::<#type_name>(task_name)
},
}
}
}
};
let expanded = quote! {
#input
#registration_code
};
Ok(expanded.into())
}