operaton_task_worker_macros/
lib.rs

1/*
2The crate [operaton_task_worker_macros] provides a proc-macro attribute macro to register an external task handler function with a name (activityId/topic).
3*/
4
5extern crate proc_macro;
6
7use proc_macro::TokenStream;
8use quote::{format_ident, quote};
9use syn::{parse_macro_input, ItemFn, Meta, Expr, Lit};
10use proc_macro_crate::{crate_name, FoundCrate};
11
12/// Attribute macro to register an external task handler function with a name (activityId/topic).
13/// Usage in a binary or library depending on `operaton-task-worker`:
14///
15/// ```ignore
16/// use operaton_task_worker_macros::task_handler;
17/// use operaton_task_worker::types::{InputVariables, OutputVariables};
18///
19/// #[task_handler(name = "example_echo")]
20/// fn echo(_input: &InputVariables) -> Result<OutputVariables, Box<dyn std::error::Error>> {
21///     Ok(std::collections::HashMap::new())
22/// }
23/// ```
24#[proc_macro_attribute]
25pub fn task_handler(attr: TokenStream, item: TokenStream) -> TokenStream {
26    // Accept a single name-value meta: name = "..."
27    let meta = parse_macro_input!(attr as Meta);
28    let input_fn = parse_macro_input!(item as ItemFn);
29
30    let name_value = match meta {
31        Meta::NameValue(nv) if nv.path.is_ident("name") => match nv.value {
32            Expr::Lit(expr_lit) => match expr_lit.lit {
33                Lit::Str(s) => s.value(),
34                _ => panic!("#[task_handler] expects name to be a string literal: name = \"...\""),
35            },
36            _ => panic!("#[task_handler] expects name to be a string literal: name = \"...\""),
37        },
38        _ => panic!("#[task_handler] requires syntax: #[task_handler(name = \"...\")]"),
39    };
40
41    let fn_ident = input_fn.sig.ident.clone();
42
43    // Resolve the runtime crate (operaton-task-worker) crate path as used by the depending crate
44    let runtime_crate_ident = match crate_name("operaton-task-worker") {
45        Ok(FoundCrate::Itself) => format_ident!("operaton_task_worker"),
46        Ok(FoundCrate::Name(name)) => format_ident!("{}", name),
47        Err(_) => format_ident!("operaton_task_worker"),
48    };
49
50    // Emit original function unchanged + inventory registration in the using crate's context
51    let expanded = quote! {
52        #input_fn
53
54        const _: () = {
55            // Ensure `inventory` is linked via the runtime crate and submit this handler
56            #runtime_crate_ident::inventory::submit! {
57                #runtime_crate_ident::registry::Handler {
58                    name: #name_value,
59                    func: #fn_ident,
60                }
61            }
62        };
63    };
64
65    TokenStream::from(expanded)
66}