Skip to main content

ruapc_macro/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{FnArg, ItemTrait, ReturnType, TraitItem, parse_macro_input};
4
5#[proc_macro_attribute]
6pub fn service(_attr: TokenStream, input: TokenStream) -> TokenStream {
7    let input = parse_macro_input!(input as ItemTrait);
8
9    let trait_ident = &input.ident;
10    let visibility = input.vis;
11    let trait_name = trait_ident.to_string();
12
13    let mut send_bounds = vec![];
14    let mut invoke_branchs = vec![];
15    let mut client_methods = vec![];
16
17    let krate = get_crate_name();
18
19    let input_items = input.items;
20    for item in &input_items {
21        if let TraitItem::Fn(method) = item
22            && method.sig.inputs.len() == 3
23            && method.sig.asyncness.is_some()
24            && let Some(receiver) = method.sig.receiver()
25            && let FnArg::Typed(req_type) = &method.sig.inputs[2]
26            && let ReturnType::Type(_, rsp_type) = &method.sig.output
27        {
28            let method_ident = &method.sig.ident;
29            if *method_ident == "ruapc_export" || *method_ident == "ruapc_request" {
30                panic!("the function cannot be named `ruapc_export` or `ruapc_request`!");
31            }
32            let method_name = format!("{trait_name}/{method_ident}");
33
34            let req_type = req_type.ty.clone();
35            let output = &method.sig.output;
36            client_methods.push(quote! {
37                async fn #method_ident(#receiver, ctx: &#krate::Context, req: #req_type) #output {
38                    self.ruapc_request(ctx, req, #method_name).await
39                }
40            });
41
42            send_bounds.push(quote! { Self::#method_ident(..): Send, });
43            invoke_branchs.push(quote! {
44                let this = self.clone();
45                let method = #krate::Method::new(
46                    ::schemars::schema_for!(#req_type),
47                    ::schemars::schema_for!(#rsp_type),
48                    Box::new(move |mut ctx, msg| {
49                        let this = this.clone();
50                        ::tokio::spawn(async move {
51                            let meta = msg.meta.clone();
52                            match msg.deserialize() {
53                                Ok(req) => {
54                                    let result = this.#method_ident(&ctx, &req).await;
55                                    ctx.send_rsp(meta, result).await;
56                                }
57                                Err(err) => {
58                                    ctx.send_err_rsp(meta, err).await;
59                                }
60                            }
61                        });
62                        Ok(())
63                    }),
64                );
65                map.insert(#method_name.into(), method);
66            });
67        } else {
68            panic!(
69                "the function should be in the form `async fn func(&self, ctx: &Context, req: &Req) -> Result<Rsp>`."
70            );
71        }
72    }
73
74    quote! {
75        #visibility trait #trait_ident {
76            const NAME: &'static str = #trait_name;
77
78            #(#input_items)*
79
80            fn ruapc_export(
81                self: ::std::sync::Arc<Self>,
82            ) -> ::std::collections::HashMap<String, #krate::Method>
83            where
84                Self: 'static + Send + Sync,
85                #(#send_bounds)*
86            {
87                let mut map = ::std::collections::HashMap::new();
88                #(#invoke_branchs)*
89                map
90            }
91        }
92
93        impl #trait_ident for #krate::Client {
94            #(#client_methods)*
95        }
96    }
97    .into()
98}
99
100pub(crate) fn get_crate_name() -> proc_macro2::TokenStream {
101    match proc_macro_crate::crate_name("ruapc") {
102        Ok(proc_macro_crate::FoundCrate::Name(name)) => {
103            let ident = syn::Ident::new(&name, proc_macro2::Span::call_site());
104            quote! { ::#ident }
105        }
106        _ => quote! { crate },
107    }
108}