nan_serve_event_subscriber/
lib.rs1extern 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 let func_name = &input_fn.sig.ident;
17
18 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 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 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 let first_param = input_fn.sig.inputs.first().expect("Function must have at least one parameter");
43
44 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 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 let expanded = quote! {
70
71 #input_fn
72
73 #check_traits
75
76 #[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 #[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 #[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}