deft_macros/
lib.rs

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                                // log::error!("invalid event type, expected type id = {:?}, actual type id = {:?}", std::any::TypeId::of::<#event_name>(), e.event_type_id());
244                            }
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    // item.self_ty.into_token_stream();
270    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 return_type = func.sig.output;
400
401    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}