1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use proc_macro::TokenStream;
use syn::{parse_macro_input, ItemFn, FnArg, PatType, Pat};
use quote::{quote};
use proc_macro2::{Ident, Span};
#[proc_macro_attribute]
pub fn get_api(_: TokenStream, item: TokenStream) -> TokenStream {
let input_fn = parse_macro_input!(item as ItemFn);
let args = input_fn.sig.inputs.clone();
let return_type = input_fn.sig.output.clone();
let input_fn_ident = input_fn.sig.ident.clone();
let raw_args = args.iter().map(|fn_arg| match fn_arg {
FnArg::Typed(PatType { pat, .. }) => match &**pat {
Pat::Ident(ident) => ident,
_ => panic!("argument pattern is not a simple ident"),
}
FnArg::Receiver(_) => panic!("argument is a receiver"),
});
let arg_idents: Vec<Ident> = raw_args.clone().map(|i| i.ident.clone()).collect();
let arg_ident_strings: Vec<String> = arg_idents.iter().map(|i| i.to_string()).collect();
let route_path = if arg_ident_strings.is_empty() {
format!("/{}", input_fn_ident.to_string())
} else {
format!("/{}/{}", input_fn_ident.to_string(), arg_ident_strings.iter().map(|ident| format!("<{}>", ident)).collect::<Vec<String>>().join("/"))
};
let input_fn_ident_string = input_fn_ident.to_string();
let request_path_strings: Vec<proc_macro2::TokenStream> = raw_args.clone().map(|arg| {
let ident = arg.ident.clone();
quote! {serde_json::to_string( & #ident ).unwrap()}
}).collect();
let request_path = if request_path_strings.is_empty() {
quote! {&format!("/{}", #input_fn_ident_string)}
} else {
quote! {&format!("http://localhost:8000/{}/{}", #input_fn_ident_string, [#(#request_path_strings),*].join("/"))}
};
let route_ident = Ident::new(&format!("{}_route", input_fn_ident_string), Span::call_site());
let request_ident = Ident::new(&format!("{}_request", input_fn_ident_string), Span::call_site());
TokenStream::from(quote!{
#input_fn
#[rocket::get(#route_path)]
pub fn #route_ident ( #(#arg_idents : String),* ) -> String {
serde_json::to_string(& #input_fn_ident ( #(serde_json::from_str(&#raw_args).unwrap()),* )).unwrap()
}
pub async fn #request_ident ( #args ) #return_type {
#[cfg(not(target_arch = "wasm32"))]
return serde_json::from_str(
&reqwest::get(#request_path)
.await.unwrap()
.text().await.unwrap()
).unwrap();
#[cfg(target_arch = "wasm32")]
return reqwasm::http::Request::get(#request_path)
.send().await.unwrap()
.json().await.unwrap();
}
})
}