1use proc_macro::TokenStream;
2use quote::{format_ident, quote, ToTokens};
3use syn::{parse_macro_input, Fields, FnArg, Ident, ImplItem, ItemFn, ItemImpl, ItemStruct, Visibility};
4use syn::__private::TokenStream2;
5use syn::token::{Async};
6
7#[proc_macro_attribute]
8pub fn mrc_object(_attr: TokenStream, struct_def: TokenStream) -> TokenStream {
9 let struct_def = parse_macro_input!(struct_def as ItemStruct);
10 let weak_name = format_ident!("{}Weak", struct_def.ident);
11 let struct_name = format_ident!("{}Data", struct_def.ident);
12 let mut_name = format_ident!("{}RefMut", struct_def.ident);
13 let ref_name = struct_def.ident;
14 let fields = struct_def.fields;
15
16 let expanded = quote! {
17
18 #[derive(Clone, PartialEq)]
19 pub struct #ref_name {
20 inner: deft::mrc::Mrc<#struct_name>,
21 }
22
23 impl std::ops::Deref for #ref_name {
24 type Target = deft::mrc::Mrc<#struct_name>;
25
26 fn deref(&self) -> &Self::Target {
27 &self.inner
28 }
29 }
30
31 impl std::ops::DerefMut for #ref_name {
32 fn deref_mut(&mut self) -> &mut Self::Target {
33 &mut self.inner
34 }
35 }
36
37 impl #ref_name {
38
39 pub fn from_inner(inner: deft::mrc::Mrc<#struct_name>) -> Self {
40 Self { inner }
41 }
42
43 pub fn as_weak(&self) -> #weak_name {
44 #weak_name {
45 inner: Some(self.inner.as_weak()),
46 }
47 }
48 }
49
50 pub struct #mut_name<'a> {
51 weak: &'a #weak_name,
52 data: #ref_name,
53 }
54
55 impl<'a> std::ops::Deref for #mut_name<'a> {
56 type Target = #ref_name;
57
58 fn deref(&self) -> &Self::Target {
59 &self.data
60 }
61 }
62
63 impl<'a> std::ops::DerefMut for #mut_name<'a> {
64 fn deref_mut(&mut self) -> &mut Self::Target {
65 &mut self.data
66 }
67 }
68
69 #[derive(Clone, PartialEq)]
70 pub struct #weak_name {
71 inner: Option<deft::mrc::MrcWeak<#struct_name>>,
72 }
73
74 impl #weak_name {
75
76 pub fn invalid() -> Self {
77 Self {inner: None}
78 }
79
80 pub fn upgrade(&self) -> Result<#ref_name, deft::mrc::UpgradeError> {
81 let inner = if let Some(inner) = &self.inner {
82 inner.upgrade()?
83 } else {
84 return Err(deft::mrc::UpgradeError{});
85 };
86 Ok(
87 #ref_name {
88 inner
89 }
90 )
91 }
92
93 pub fn upgrade_mut(&self) -> Result<#mut_name, deft::mrc::UpgradeError> {
94 let data = self.upgrade()?;
95 Ok(
96 #mut_name {
97 weak: self,
98 data,
99 }
100 )
101 }
102
103 }
104
105 pub struct #struct_name
106 #fields
107
108
109 impl #struct_name {
110 pub fn to_ref(self) -> #ref_name {
111 let inner = deft::mrc::Mrc::new(self);
112 #ref_name::from_inner(inner)
113 }
114 }
115
116 };
117 expanded.into()
118}
119
120#[proc_macro_attribute]
121pub fn element_backend(_attr: TokenStream, struct_def: TokenStream) -> TokenStream {
122 let struct_def = parse_macro_input!(struct_def as ItemStruct);
123 let struct_name = struct_def.ident.clone();
124 let visibility = struct_def.vis.clone();
125 let struct_fields = struct_def.fields;
126 let q = quote! {
127
128 #[deft_macros::mrc_object]
129 #visibility struct #struct_name #struct_fields
130
131 impl deft::js::FromJsValue for #struct_name {
132 fn from_js_value(value: deft::js::JsValue) -> Result<Self, deft::js::ValueError> {
133 let element = deft::element::Element::from_js_value(value)?;
134 Ok(element.get_backend_as::<#struct_name>().clone())
135 }
136 }
137 };
138 q.into()
139}
140
141#[proc_macro_attribute]
142pub fn event(_attr: TokenStream, struct_def: TokenStream) -> TokenStream {
143 create_event(_attr, struct_def, quote! {deft::element::ElementWeak})
144}
145
146#[proc_macro_attribute]
147pub fn window_event(_attr: TokenStream, struct_def: TokenStream) -> TokenStream {
148 create_event(_attr, struct_def, quote! {deft::window::WindowHandle})
149}
150
151#[proc_macro_attribute]
152pub fn worker_event(_attr: TokenStream, struct_def: TokenStream) -> TokenStream {
153 create_event(_attr, struct_def, quote! {deft::ext::ext_worker::WorkerWeak})
154}
155
156#[proc_macro_attribute]
157pub fn worker_context_event(_attr: TokenStream, struct_def: TokenStream) -> TokenStream {
158 create_event(_attr, struct_def, quote! {deft::ext::ext_worker::WorkerContextWeak})
159}
160
161fn create_event(_attr: TokenStream, struct_def: TokenStream, target_type: TokenStream2) -> TokenStream {
162 let struct_def = parse_macro_input!(struct_def as ItemStruct);
163 let listener_name = format_ident!("{}Listener", struct_def.ident);
164 let event_name = struct_def.ident;
165 let fields = struct_def.fields;
166
167 let fields_ts = match fields {
168 Fields::Named(nf) => { quote! {#nf} }
169 Fields::Unnamed(uf) => { quote! {#uf;} }
170 Fields::Unit => {quote! {#fields;} }
171 };
172
173 let expanded = quote! {
174
175 pub struct #listener_name(Box<dyn FnMut(&mut #event_name, &mut deft::base::EventContext<#target_type>)>);
176
177 impl #listener_name {
178 pub fn new<F: FnMut(&mut #event_name, &mut deft::base::EventContext<#target_type>) + 'static>(f: F) -> Self {
179 Self(Box::new(f))
180 }
181 }
182
183 impl deft::base::EventListener<#event_name, #target_type> for #listener_name {
184 fn handle_event(&mut self, event: &mut #event_name, ctx: &mut deft::base::EventContext<#target_type>) {
185 (self.0)(event, ctx)
186 }
187 }
188
189 impl deft::js::FromJsValue for #listener_name {
190 fn from_js_value(value: deft::js::JsValue) -> Result<Self, deft::js::ValueError> {
191 let listener = Self::new(move |e, ctx| {
192 let target = ctx.target.clone();
193 use deft::js::ToJsValue;
194 if let Ok(d) = target.to_js_value() {
195 if let Ok(e) = e.clone().to_js_value() {
196 let callback_result = value.call_as_function(vec![e, d]);
197 if let Ok(cb_result) = callback_result {
198 if let Ok(res) = deft::js::js_value_util::EventResult::from_js_value(cb_result) {
199 if res.propagation_cancelled {
200 ctx.propagation_cancelled = true;
201 }
202 if res.prevent_default {
203 ctx.prevent_default = true;
204 }
205 }
206 }
207 } else {
208 println!("invalid event");
209 }
210 } else {
211 println!("invalid event");
212 }
213 });
214 Ok(listener)
215 }
216 }
217
218 #[derive(serde::Serialize, Clone)]
219 #[serde(rename_all = "camelCase")]
220 pub struct #event_name
221 #fields_ts
222
223 deft::js_serialize!(#event_name);
224
225 impl deft::element::ViewEvent for #event_name {
226 fn allow_bubbles(&self) -> bool {
227 true
228 }
229 }
230
231 impl deft::base::JsEvent<#target_type> for #event_name {
232 fn create_listener_factory() -> deft::base::BoxJsEventListenerFactory<#target_type> {
233 use deft::base::EventListener;
234 use deft::js::js_binding::FromJsValue;
235 Box::new(move |listener| {
236 let mut listener = <#listener_name>::from_js_value(listener).ok()?;
237 Some((
238 std::any::TypeId::of::<#event_name>(),
239 Box::new(move |e, ctx| {
240 if let Some(e) = e.downcast_mut::<#event_name>() {
241 listener.handle_event(e, ctx);
242 } else {
243 }
245 }),
246 ))
247 })
248 }
249 }
250
251 impl #event_name {
252 pub fn cast(event: &deft::event::Event) -> Option<&Self> {
253 event.downcast_ref::<Self>()
254 }
255
256 pub fn is(event: &deft::event::Event) -> bool {
257 Self::cast(event).is_some()
258 }
259
260 }
261
262 };
263 expanded.into()
264}
265
266#[proc_macro_attribute]
267pub fn js_methods(_attr: TokenStream, impl_item: TokenStream) -> TokenStream {
268 let item = parse_macro_input!(impl_item as ItemImpl);
269 let ItemImpl {
271 attrs,
272 impl_token,
273 generics,
274 self_ty,
275 mut items,
276 ..
277 } = item;
278
279 let mut api_bridges = Vec::new();
280 let mut api_create_expr_list = Vec::new();
281 let type_name_str = self_ty.clone().into_token_stream().to_string();
282 let type_name_ident = format_ident!("{}", type_name_str);
283
284
285 for item in &mut items {
286 match item {
287 ImplItem::Fn(item) => {
288 item.attrs.retain(|it| {
289 if !it.path().is_ident("js_func") {
290 return true;
291 }
292
293 let vis = item.vis.clone();
294
295 let api_name_ident = format_ident!("{}_{}", type_name_str, item.sig.ident);
296
297 let args_count = item.sig.inputs.len();
298 let args = item.sig.inputs.iter().map(|it| it.clone()).collect::<Vec<_>>();
299
300 let bridge_body = build_bridge_body(
301 args,
302 item.sig.asyncness,
303 type_name_ident.clone(),
304 item.sig.ident.clone()
305 );
306
307 let bridge = build_bridge_struct(
308 vis,
309 api_name_ident.clone(),
310 args_count,
311 bridge_body,
312 );
313
314 api_bridges.push(bridge);
315 api_create_expr_list.push(quote! {
316 #api_name_ident::new()
317 });
318 false
319 });
320 }
321 _ => {}
322 }
323 }
324 let q = quote! {
325 #(#attrs)*
326 #impl_token #generics #self_ty {
327 #(#items)*
328
329 pub fn create_js_apis() -> Vec<Box<dyn deft::js::JsFunc + std::panic::RefUnwindSafe + 'static>> {
330 vec![#(Box::new(#api_create_expr_list), )*]
331 }
332
333 }
334
335 #(#api_bridges)*
336 };
337
338 q.into()
339}
340
341fn build_bridge_struct(vis: Visibility, func_name: Ident, args_count: usize, bridge_body: TokenStream2) -> TokenStream2 {
342 let func_name_str = func_name.to_string();
343 quote! {
344 #[doc(hidden)]
345 #[allow(nonstandard_style)]
346 #vis struct #func_name {}
347
348 impl #func_name {
349 pub fn new() -> Self {
350 Self {}
351 }
352 }
353
354 impl deft::js::JsFunc for #func_name {
355
356 fn name(&self) -> &str {
357 #func_name_str
358 }
359
360 fn args_count(&self) -> usize {
361 #args_count
362 }
363
364 fn call(&self, js_context: &mut deft::mrc::Mrc<deft::js::JsContext>, args: Vec<deft::js::JsValue>) -> Result<deft::js::JsValue, deft::js::JsCallError> {
365 #bridge_body
366 }
367 }
368 }
369}
370
371fn build_bridge_body(func_inputs: Vec<FnArg>, asyncness: Option<Async>, struct_name: Ident, func_name: Ident) -> TokenStream2 {
372 let mut receiver = None;
373 let mut params = Vec::new();
374 func_inputs.iter().for_each(|i| {
375 match i {
376 FnArg::Receiver(r) => receiver = Some(r.ty.clone()),
377 FnArg::Typed(ref val) => {
378 params.push(val.ty.clone())
379 }
380 }
381 });
382 let mut param_expand_stmts = Vec::new();
383 let mut param_list = Vec::new();
384 let mut idx = if receiver.is_some() { 1usize } else { 0usize };
385 for p in params {
386 let p_name = format_ident!("_p{}", idx);
387 param_expand_stmts.push(quote! {
388 let #p_name = <#p as deft::js::FromJsValue>::from_js_value(args.get(#idx).unwrap().clone())
389 .map_err(
390 |e| deft::js::ValueError::Internal(
391 format!("Failed to cast js argument {} (zero-based) to rust type, {}", #idx, e)
392 )
393 )?;
394 });
395 param_list.push(p_name);
396 idx += 1;
397 }
398
399 let call_stmt = if asyncness.is_none() {
402 if receiver.is_some() {
403 quote! {
404 let inst_js_value = args.get(0).unwrap().clone();
405 let r = <#struct_name as deft::js::BorrowFromJs>::borrow_from_js(inst_js_value, move |inst| {
406 inst.#func_name( #(#param_list, )* )
407 })?;
408 }
409 } else {
410 quote! {
411 let r = #struct_name::#func_name( #(#param_list, )* );
412 }
413 }
414 } else {
415 if receiver.is_some() {
416 quote! {
417 let mut inst = <#struct_name as deft::js::FromJsValue>::from_js_value(args.get(0).unwrap().clone())?;
418 let r = js_context.create_async_task2(async move {
419 inst.#func_name( #(#param_list, )* ).await
420 });
421 }
422 } else {
423 quote! {
424 let r = js_context.create_async_task2(async move {
425 #struct_name::#func_name( #(#param_list, )* ).await
426 });
427 }
428 }
429 };
430 let result = quote! {
431 use deft::js::FromJsValue;
432 use deft::js::ToJsValue;
433 use deft::js::ToJsCallResult;
434 #(#param_expand_stmts)*
435 #call_stmt
436 r.to_js_call_result()
437 };
438 result
439}
440
441#[proc_macro_attribute]
442pub fn js_func(_attr: TokenStream, func: TokenStream) -> TokenStream {
443 let func = parse_macro_input!(func as ItemFn);
444 let vis = func.vis;
445 let func_name = &func.sig.ident;
446 let asyncness = func.sig.asyncness;
447 let func_inputs = func.sig.inputs;
448 let func_block = func.block;
449
450 let args_count = func_inputs.len();
451 let args = func_inputs.iter().map(|it| it.clone()).collect::<Vec<_>>();
452 let bridge_body = build_bridge_body(
453 args,
454 asyncness,
455 format_ident!("Self"),
456 func_name.clone()
457 );
458
459 let return_type = func.sig.output;
460
461 let bridge = build_bridge_struct(vis, func_name.clone(), args_count, bridge_body);
462
463 let expanded = quote! {
464
465 #bridge
466
467 impl #func_name {
468
469 #asyncness fn #func_name(#func_inputs) #return_type #func_block
470
471 }
472
473 };
474 expanded.into()
475}