nan_serve_event_subscriber/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::{quote, format_ident, ToTokens};
5use syn::{
6    parse_macro_input, FnArg, PatType, ItemFn,
7    spanned::Spanned
8};
9
10
11#[proc_macro_attribute]
12pub fn subscribe_to_event(_attr: TokenStream, item: TokenStream) -> TokenStream {
13    let input_fn = parse_macro_input!(item as ItemFn);
14
15    // Get the function name
16    let func_name = &input_fn.sig.ident;
17    
18    // Generate new function names
19    let register_func_name = format_ident!("register_{}", func_name);
20    let init_func_name = format_ident!("init_{}", func_name);
21    let routed_func_name = format_ident!("routed_{}", func_name);
22    let check_func_name = format_ident!("_check_{}", func_name);
23
24    // Ensure the function is async
25    if input_fn.sig.asyncness.is_none() {
26        return syn::Error::new(input_fn.sig.asyncness.span(), "Function must be async")
27            .to_compile_error()
28            .into();
29    }
30
31    // Ensure the function has exactly one parameter
32    if input_fn.sig.inputs.len() != 1 {
33        return syn::Error::new(
34            input_fn.sig.inputs.span(), 
35            "Function must have exactly one parameter which is the message struct that it is subscribing to"
36        )
37            .to_compile_error()
38            .into();
39    }
40
41    // Get the first argument (if any)
42    let first_param = input_fn.sig.inputs.first().expect("Function must have at least one parameter");
43
44    // Ensure the first parameter is a typed argument
45    let param_type = if let FnArg::Typed(PatType { ty, .. }) = first_param {
46        ty
47    } else {
48        return syn::Error::new(first_param.span(), "Expected a typed parameter")
49            .to_compile_error()
50            .into();
51    };
52    let param_name = param_type.to_token_stream().to_string().trim_matches('"').to_string();
53
54    // Generate trait-bound verification code
55    let check_traits = quote! {
56        #[doc(hidden)]
57        fn #check_func_name<T>()
58        where
59            T: serde::Serialize + serde::de::DeserializeOwned,
60        {}
61        #[doc(hidden)]
62        const _: fn() = || {
63            #check_func_name::<#param_type>();
64        };
65    };
66
67
68    // Generate the expanded code
69    let expanded = quote! {
70
71        #input_fn
72
73        // Inline trait checks
74        #check_traits
75
76        // Define a router function that accepts bincode and returns a boxed future
77        #[doc(hidden)]
78        fn #routed_func_name(data: Vec<u8>) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>> {
79            std::boxed::Box::pin(async move {
80                let deserialized: #param_type = nanoservices_utils::bincode::deserialize(&data).unwrap();
81                #func_name(deserialized).await;
82            })
83        }
84    
85        // Register function
86        #[doc(hidden)]
87        fn #register_func_name() {
88            crate::tokio_event_adapter_runtime::insert_into_hashmap(
89                #param_name.to_string(),
90                #routed_func_name
91            );
92        }
93
94        // Init function
95        #[nanoservices_utils::ctor::ctor]
96        fn #init_func_name() {
97            println!("Initializing function: {}", stringify!(#func_name));
98            #register_func_name();
99        }
100    };
101
102    TokenStream::from(expanded)
103}