Skip to main content

onebot_api_macro/
lib.rs

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]
77// pub fn auto_send(args: TokenStream, input: TokenStream) -> TokenStream {
78// 	let input_fn = parse_macro_input!(input as ItemFn);
79// 	let sig = &input_fn.sig;
80// 	let body = &input_fn.block;
81// 	let action_name = &sig.ident;
82// 	let inputs = &sig.inputs.iter().filter(|input| {
83// 		if let FnArg::Typed(input) = input && let Pat::Ident(ident) = &*input.pat {
84// 			return true
85// 		}
86// 		false
87// 	}).collect::<Punctuated<_, Token![,]>>();
88// 	let args = sig.inputs.iter().filter(|arg| {
89// 		if let FnArg::Typed(arg) = arg && let Pat::Ident(ident) = &*arg.pat {
90// 			return true
91// 		}
92// 		false
93// 	}).map(|arg| {
94// 		if let FnArg::Typed(arg) = arg && let Pat::Ident(ident) = &*arg.pat {
95// 			return Some(ident.ident.clone())
96// 		}
97// 		None
98// 	}).map(|arg| arg.unwrap()).collect::<Vec<_>>();
99//
100// 	let ident_stmts = args.iter().map(|ident| quote! {
101// 		#ident
102// 	});
103//
104// 	let send_msg_stmt = quote! {
105// 		::macros::auto_send_ws_msg!(
106// 			#action_name
107// 			#(#ident_stmts)*
108// 		)
109// 	};
110//
111// 	let expended = quote! {
112// 		#sig {
113// 			async fn __inner<T>(#inputs) -> ::anyhow::Result<T> {
114// 				#send_msg_stmt
115// 			}
116// 			#body
117// 		}
118// 	};
119// 	expended.into()
120// }
121
122#[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}