Newer
Older
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
mod api_def;
#[proc_macro]
pub fn rpc_api(input_token_stream: TokenStream) -> TokenStream {
// Start by parsing the input into what we expect.
let defs: api_def::ApiDefinitions = syn::parse(input_token_stream)
.expect("failed to parse input");
let out: Vec<_> = defs.apis.into_iter().map(build_api).collect();
TokenStream::from(quote!{
#(#out)*
})
}
fn build_api(api: api_def::ApiDefinition) -> proc_macro2::TokenStream {
let enum_name = &api.name;
let mut variants = Vec::new();
for function in &api.definitions {
let variant_name = &function.ident;
let ret = match &function.output {
syn::ReturnType::Default => quote!{()},
syn::ReturnType::Type(_, ty) => quote!{#ty},
};
variants.push(quote!{
#variant_name {
respond: jsonrpsee::core::server::TypedResponder<'a, R, I, #ret>,
}
});
}
let next_request = {
let mut function_blocks = Vec::new();
for function in &api.definitions {
let f_name = &function.ident;
let ret_ty = &function.output;
let rpc_method_name = function.ident.to_string();
function_blocks.push(quote!{
if method == #rpc_method_name {
let request = server.request_by_id(&request_id).unwrap();
/*$(
let $pn: $pty = {
let raw_val = match request.params().get(stringify!($pn)) {
Some(v) => v,
None => {
request.respond(Err(jsonrpsee::core::common::Error::invalid_params("foo"))).await; // TODO: message
continue;
}
};
match jsonrpsee::core::common::from_value(raw_val.clone()) {
Ok(v) => v,
Err(_) => {
request.respond(Err(jsonrpsee::core::common::Error::invalid_params("foo"))).await; // TODO: message
continue;
}
}
};
)**/
let respond = jsonrpsee::core::server::TypedResponder {
rq: request,
response_ty: std::marker::PhantomData,
};
return Ok(#enum_name::#f_name { respond });
}
});
}
quote! {
async fn next_request(server: &'a mut jsonrpsee::core::server::Server<R, I>) -> Result<#enum_name<'a, R, I>, std::io::Error>
where R: jsonrpsee::core::server::raw::RawServer<RequestId = I>,
I: Clone + PartialEq + Eq + Send + Sync,
{
loop {
let (request_id, method) = match server.next_event().await.unwrap() { // TODO: don't unwrap
jsonrpsee::core::server::ServerEvent::Notification(n) => unimplemented!(), // TODO:
jsonrpsee::core::server::ServerEvent::Request(r) => (r.id(), r.method().to_owned()),
};
#(#function_blocks)*
server.request_by_id(&request_id).unwrap().respond(Err(jsonrpsee::core::common::Error::method_not_found())).await;
}
}
}
};
let mut client_functions = Vec::new();
for function in &api.definitions {
let f_name = &function.ident;
let ret_ty = &function.output;
let rpc_method_name = function.ident.to_string();
client_functions.push(quote!{
async fn #f_name() #ret_ty {
/*$(
let $pn = jsonrpsee::common::to_value($pn).unwrap(); // TODO: don't unwrap
)**/
let http = jsonrpsee::http_client("http://localhost:8000");
http.request(#rpc_method_name).await.unwrap()
}
});
}
quote! {
enum #enum_name<'a, R, I> {
#(#variants),*
}
impl<'a, R, I> #enum_name<'a, R, I> {
#next_request
}
// TODO: move inside of the impl block, but then you've got the question of how to handle
// inferring generics
#(#client_functions)*
}
}