1use proc_macro::{TokenStream, TokenTree};
2use proc_macro2::Span;
3use quote::quote;
4use serde_json::{Value, json};
5use std::collections::HashMap;
6use syn::parse::{Parse, ParseStream};
7use syn::punctuated::Punctuated;
8use syn::{FnArg, Ident, ItemFn, LitStr, Pat, Token, parse_macro_input};
9
10struct IdentList {
11 idents: Vec<Ident>,
12}
13
14#[derive(Clone)]
15struct IdentName {
16 pub name: LitStr,
17 pub ident: Ident,
18}
19
20impl Parse for IdentList {
21 fn parse(input: ParseStream) -> syn::Result<Self> {
22 let mut idents = Vec::new();
23 while !input.is_empty() {
24 let ident = input.parse::<Ident>()?;
25 idents.push(ident);
26 }
27 Ok(IdentList { idents })
28 }
29}
30
31#[proc_macro]
32pub fn auto_send_ws_msg(input: TokenStream) -> TokenStream {
33 let IdentList { idents } = parse_macro_input!(input as IdentList);
34 let ident_name_list: Vec<IdentName> = idents
35 .iter()
36 .map(|ident| IdentName {
37 name: LitStr::new(&ident.to_string(), Span::call_site()),
38 ident: ident.clone(),
39 })
40 .collect();
41 let (action, params) = ident_name_list.split_at(1);
42 let action = &action[0];
43 let action_name = action.name.clone();
44 let insert_stmts = params
45 .iter()
46 .map(|ident| {
47 let k = ident.name.clone();
48 let v = ident.ident.clone();
49 quote! {
50 map.insert(#k.to_string(), ::serde_json::to_value(#v)?);
51 }
52 })
53 .collect::<Vec<_>>();
54 let expended = quote! {
55 {
56 let mut map: ::std::collections::HashMap<String, ::serde_json::Value> = ::std::collections::HashMap::new();
57 #(#insert_stmts)*
58
59 let uuid = ::uuid::Uuid::new_v4().to_string();
60 let call_json = ::serde_json::to_string(&Self::generate_api_call_json(#action_name.to_string(), map, uuid.clone()))?;
61
62 let mut receiver = self.get_receiver();
63 let max_waiting_times = self.max_waiting_times.clone();
64 let task = ::tokio::spawn(async move {
65 Self::wait_for_echo(&mut receiver, uuid, Some(max_waiting_times)).await
66 });
67
68 self.api_sender.send_async(call_json).await?;
69
70 task.await?
71 }
72 };
73 expended.into()
74}
75
76#[proc_macro_attribute]
123pub fn generate_json(args: TokenStream, input: TokenStream) -> TokenStream {
124 let input_fn = parse_macro_input!(input as ItemFn);
125 let sig = &input_fn.sig;
126 let body = &input_fn.block;
127 let action_name = &sig.ident;
128 let action_name_str = LitStr::new(action_name.to_string().as_str(), Span::call_site());
129 let args = sig
130 .inputs
131 .iter()
132 .filter(|arg| {
133 if let FnArg::Typed(arg) = arg
134 && let Pat::Ident(ident) = &*arg.pat
135 {
136 return true;
137 }
138 false
139 })
140 .map(|arg| {
141 if let FnArg::Typed(arg) = arg
142 && let Pat::Ident(ident) = &*arg.pat
143 {
144 return Some(ident.ident.clone());
145 }
146 None
147 })
148 .map(|arg| arg.unwrap())
149 .collect::<Vec<_>>();
150
151 let params = args
152 .iter()
153 .map(|arg| IdentName {
154 name: LitStr::new(arg.to_string().as_str(), Span::call_site()),
155 ident: arg.clone(),
156 })
157 .collect::<Vec<_>>();
158
159 let insert_stmts = params
160 .iter()
161 .map(|ident| {
162 let k = ident.name.clone();
163 let v = ident.ident.clone();
164 quote! {
165 map.insert(#k.to_string(), ::serde_json::to_value(&#v).unwrap());
166 }
167 })
168 .collect::<Vec<_>>();
169
170 let expended = quote! {
171 #sig {
172 let __echo = ::uuid::Uuid::new_v4().to_string();
173 let __json = Self::generate_api_call_json(#action_name_str.to_string(), {
174 let mut map: ::std::collections::HashMap<String, ::serde_json::Value> = ::std::collections::HashMap::new();
175 #(#insert_stmts)*
176 map
177 }, __echo.clone());
178 #body
179 }
180 };
181 expended.into()
182}