1use core::panic;
2use darling::FromDeriveInput;
3use proc_macro::{self, TokenStream};
4use quote::{format_ident, quote};
5use std::hash::Hash;
6use syn::{parse_macro_input, Data, DataEnum, DataStruct, DeriveInput, TypePath};
7
8#[derive(FromDeriveInput, Default)]
10#[darling(default, attributes(rpc_request))]
11struct Opts {
12 namespace: String,
14 response: Option<String>,
15}
16
17#[proc_macro_derive(RpcRequest, attributes(rpc_request))]
18pub fn derive_rpc_req(input: TokenStream) -> TokenStream {
19 let input = parse_macro_input!(input);
20 let opts = Opts::from_derive_input(&input).expect("Wrong options");
21 let DeriveInput { ident, data, .. } = input;
22 match data {
23 syn::Data::Struct(DataStruct { fields, .. }) => {
24 let name = format!("{ident}");
25 let name_no_suffix = name
26 .strip_suffix("Request")
27 .expect("make sure to put 'Request' at the end of your struct name");
28 let first_char = name_no_suffix
30 .chars()
31 .next()
32 .unwrap()
33 .to_owned()
34 .to_lowercase();
35 let method = format!("{first_char}{}", &name_no_suffix[1..]);
36
37 let mut from_json_body = quote! {};
38 let mut create_self_body = quote! {};
39
40 for f in fields {
41 let id = f.ident.unwrap();
42 let json_name = format_ident!("{}_json", id);
43 let id_string = format!("{id}");
44 let not_exist = format!("field '{id_string}' does not exist");
45 let not_deserialize = format!("field '{id_string}' does not implement deserialize");
46 from_json_body = quote! {
47 #from_json_body
48 let #json_name = json.get(#id_string).ok_or(#not_exist)?.to_owned();
49 let #id = serde_json::from_value(#json_name).map_err(|_|#not_deserialize)?;
50 };
51
52 create_self_body = quote! {
53 #create_self_body
54 #id,
55 }
56 }
57
58 let create_self = quote! {
59 Ok(Self {
60 #create_self_body
61 })
62 };
63
64 let from_json = quote! {
65 fn try_from_json(json: &serde_json::Value) -> std::result::Result<Self,Box<dyn std::error::Error + Send + Sync + 'static>> {
66 #from_json_body
67 #create_self
68 }
69 };
70
71 let method_name = quote! {
72 fn method()-> &'static str {
73 #method
74 }
75 };
76
77 let ns = opts.namespace;
78 let (ns_type, ns_var) = ns
79 .split_once(':')
80 .expect("expected namespace attribute to have a ':'");
81
82 let ns_type_id = format_ident!("{ns_type}");
83 let namespace = quote! {
84 fn namespace() -> Self::Namespace {
85 Self::Namespace::try_from_str(#ns_var).unwrap()
86
87 }
88 };
89
90 let (response_struct_name, should_impl) = match opts.response {
91 Some(res) => (format_ident!("{}", res), false),
94 None => (format_ident!("{}Response", name_no_suffix), true),
95 };
96
97 let mut output = quote! {};
98 let response_struct_id = format!("{response_struct_name}").to_lowercase();
99 if should_impl {
100 output = quote! {
101 impl RpcResponse for #response_struct_name {
102 const IDENTITY: &str = #response_struct_id;
103 }
104 }
105 }
106 output = quote! {
107 #output
108 impl RpcRequest for #ident {
109 type Response = #response_struct_name;
110 type Namespace = #ns_type_id;
111 #from_json
112 #method_name
113 #namespace
114 }
115 };
116
117 output.into()
118 }
119 _ => {
120 panic!("cannot derive this on anything but a struct")
121 }
122 }
123}
124
125#[proc_macro_derive(RequestWrapper)]
126pub fn derive_req_wrapper(input: TokenStream) -> TokenStream {
127 let input = parse_macro_input!(input);
128 let DeriveInput { ident, data, .. } = input;
129 match data {
130 Data::Enum(DataEnum { variants, .. }) => {
131 let mut from_impls = quote! {};
132 let mut into_req_body = quote! {};
133 let mut from_req_body = quote! {
134 let e:Box<dyn std::error::Error + Send + Sync + 'static> = std::io::Error::other("Could not get Request object").into();
135 let mut ret = Err(e);
136 };
137 for v in variants {
138 let id = v.ident;
139 let enum_typ = match v.fields {
140 syn::Fields::Unnamed(t) => match t.unnamed.iter().next().cloned().unwrap().ty {
141 syn::Type::Path(TypePath { path, .. }) => {
142 path.segments.iter().next().unwrap().ident.clone()
143 }
144 other => panic!("Expected type path as unnamed variant, got: {other:#?}"),
145 },
146 _ => panic!("only unnamed struct variants supported"),
147 };
148 let not_request = format!("variant {id} does not implement RpcRequest");
149
150 into_req_body = quote! {
151 #into_req_body
152 Self::#id(r) => r.into_request(id).expect(#not_request),
153 };
154
155 from_req_body = quote! {
156 #from_req_body
157 if ret.is_err() {
158 match #enum_typ::try_from_request(&req) {
159 Ok(v) => return Ok(Self::#id(v)),
160 Err(e) => ret = Err(e),
161 }
162 }
163 };
164
165 from_impls = quote! {
166 #from_impls
167 impl From<#enum_typ> for #ident {
168 fn from(v: #enum_typ) -> Self {
169 Self::#id(v)
170 }
171 }
172 };
173 }
174
175 let into_req = quote! {
176 fn into_req(&self, id: impl ToString) -> seraphic::Request {
177 match self {
178 #into_req_body
179 }
180 }
181 };
182
183 let from_req = quote! {
184 fn try_from_req(req: seraphic::Request) -> std::result::Result<Self,Box<dyn std::error::Error + Send + Sync + 'static>> {
185 #from_req_body
186 return ret;
187 }
188 };
189
190 let output = quote! {
191 #from_impls
192 impl seraphic::RequestWrapper for #ident {
193 #into_req
194 #from_req
195
196 }
197 };
198 output.into()
199 }
200 _ => {
201 panic!("cannot derive this on anything but an enum")
202 }
203 }
204}
205
206#[proc_macro_derive(ResponseWrapper)]
207pub fn derive_res_wrapper(input: TokenStream) -> TokenStream {
208 let input = parse_macro_input!(input);
209 let DeriveInput { ident, data, .. } = input;
210 match data {
211 Data::Enum(DataEnum { variants, .. }) => {
212 let mut from_impls = quote! {};
213 let mut into_res_body = quote! {};
214 let mut from_res_body = quote! {
215 let e:Box<dyn std::error::Error + Send + Sync + 'static> = std::io::Error::other("Could not get Response object").into();
216 let mut ret = Err(e);
217 };
218 for v in variants {
219 let id = v.ident;
220 let enum_typ = match v.fields {
221 syn::Fields::Unnamed(t) => match t.unnamed.iter().next().cloned().unwrap().ty {
222 syn::Type::Path(TypePath { path, .. }) => {
223 path.segments.iter().next().unwrap().ident.clone()
224 }
225 other => panic!("Expected type path as unnamed variant, got: {other:#?}"),
226 },
227 _ => panic!("only unnamed struct variants supported"),
228 };
229 let not_res = format!("variant {id} does not implement RpcResponse");
230
231 into_res_body = quote! {
232 #into_res_body
233 Self::#id(r) => r.into_response(id).expect(#not_res),
234 };
235
236 from_res_body = quote! {
237 #from_res_body
238 if ret.is_err() {
239 ret = #enum_typ::try_from_response(&res).map(|maybe_ok| maybe_ok.map(|ok| Self::#id(ok)));
240 }
241 };
242
243 from_impls = quote! {
244 #from_impls
245 impl From<#enum_typ> for #ident {
246 fn from(v: #enum_typ) -> Self {
247 Self::#id(v)
248 }
249 }
250 };
251 }
252
253 let into_res = quote! {
254 fn into_res(&self, id: impl ToString) -> seraphic::IdentifiedResponse {
255 match self {
256 #into_res_body
257 }
258 }
259 };
260
261 let from_res = quote! {
262 fn try_from_res(res: seraphic::IdentifiedResponse) -> std::result::Result<std::result::Result<Self, seraphic::error::Error>, Box<dyn std::error::Error + Send + Sync + 'static>> {
263 #from_res_body
264 return ret;
265 }
266 };
267
268 let output = quote! {
269 #from_impls
270 impl ResponseWrapper for #ident {
271 #into_res
272 #from_res
273
274 }
275 };
276 output.into()
277 }
278 _ => {
279 panic!("cannot derive this on anything but an enum")
280 }
281 }
282}
283
284#[derive(FromDeriveInput, Default)]
285#[darling(default, attributes(namespace))]
286struct NamespaceOpts {
287 separator: Option<String>,
288}
289
290#[proc_macro_derive(RpcNamespace, attributes(namespace))]
291pub fn derive_namespace(input: TokenStream) -> TokenStream {
292 let input = parse_macro_input!(input);
293 let opts = NamespaceOpts::from_derive_input(&input).expect("Wrong options");
294 let separator = opts.separator.unwrap_or("_".to_string());
295 let separator = quote! {const SEPARATOR: &str = #separator;};
296
297 let DeriveInput { ident, data, .. } = input;
298 match data {
299 Data::Enum(DataEnum { variants, .. }) => {
300 let mut from_str_body = quote! {};
301 let mut as_ref_body = quote! {};
302 let mut my_str_consts = quote! {};
303 for v in variants {
304 let id = v.ident;
305 let id_str = format!("{id}");
306 let const_id = format_ident!("{}", id_str.to_uppercase());
307 let const_val = id_str.to_lowercase();
308 my_str_consts = quote! {
309 #my_str_consts
310 const #const_id: &str = #const_val;
311 };
312 from_str_body = quote! {
313 #from_str_body
314 Self::#const_id => Some(Self::#id),
315 };
316 as_ref_body = quote! {
317 #as_ref_body
318 Self::#id => Self::#const_id,
319 };
320 }
321
322 let as_str = quote! {
323 fn as_str(&self)-> &str {
324 match self {
325 #as_ref_body
326 }
327 }
328 };
329
330 let try_from = quote! {
331 fn try_from_str(str: &str) -> Option<Self> {
332 match str {
333 #from_str_body
334 o => None,
335 }
336 }
337 };
338
339 let output = quote! {
340 impl #ident {
341 #my_str_consts
342 }
343 impl RpcNamespace for #ident {
344 #separator
345 #as_str
346 #try_from
347 }
348 };
349
350 output.into()
351 }
352 _ => {
353 panic!("cannot derive this on anything but an enum")
354 }
355 }
356}