yarte_codegen 0.15.7

Code generator for yarte
Documentation
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{parse2, punctuated::Punctuated, ExprCall, ExprPath, ExprStruct, Ident, Token};

use yarte_dom::dom::ExprId;

use super::{utils::get_vdom_ident, BlackBox, Parent, WASMCodeGen};

// TODO: attribute prevent default, event and event type
#[allow(unused_variables)]
fn get_closure(msg: &syn::Expr) -> (TokenStream, TokenStream) {
    use syn::Expr::*;
    let (msg, cloned) = match msg {
        Path(ExprPath { attrs, qself, path }) => (quote!(#msg), quote!()),
        Call(ExprCall {
            attrs, func, args, ..
        }) => {
            let mut new: Punctuated<Ident, Token![,]> = Punctuated::new();
            let mut cloned = TokenStream::new();
            for (count, arg) in args.iter().enumerate() {
                let ident = format_ident!("__cloned__{}", count);
                cloned.extend(quote!(let #ident = (#arg).clone();));
                new.push(ident);
            }
            (quote!(#func(#new)), cloned)
        }
        Struct(ExprStruct {
            attrs,
            path,
            fields,
            dot2_token,
            rest,
            ..
        }) => todo!("message struct"),
        _ => panic!("no valid expression at `on` attribute"),
    };
    (
        quote! {
            Closure::wrap(Box::new(move |__event: yarte_wasm_app::web::Event| {
                    __event.prevent_default();
                    __addr.send(#msg);
                }) as Box<dyn Fn(yarte_wasm_app::web::Event)>)
        },
        cloned,
    )
}

impl<'a> WASMCodeGen<'a> {
    pub(super) fn write_event(&mut self, id: ExprId, event: &str, msg: &syn::Expr) {
        let name = self.current_node_ident(0);
        assert_eq!(&event[..2], "on");
        let event = &event[2..];
        let vars = self.solver.expr_inner_var(&id);

        let (forget, dom) = match self.cur().id {
            Parent::Body => {
                let ident = self.global_bb_ident();
                (vars.is_empty(), quote!(self.#ident))
            }
            Parent::Expr(i) => {
                let ident = get_vdom_ident(i);
                (false, quote!(#ident))
            }
            Parent::Head => todo!(),
        };

        // Make closure expression
        let (closure_expr, clones) = get_closure(msg);

        if forget {
            let cur = self.cur_mut();
            cur.buff_hydrate.push(quote! {
                #clones
                let __cloned__ = __addr.clone();
                let __closure__ = #closure_expr;
                #name
                    .add_event_listener_with_callback(#event, yarte_wasm_app::JsCast::unchecked_ref(__closure__.as_ref()))
                .unwrap_throw();
                __closure__.forget();
            });
            cur.path_events.push((name, cur.steps.clone()));
        } else {
            let closure = format_ident!("__closure__{}", self.count);
            self.count += 1;
            let current = self.stack.last_mut();
            current.black_box.push(BlackBox {
                doc: "".to_string(),
                name: closure.clone(),
                ty: parse2(quote!(Option<Closure<dyn Fn(yarte_wasm_app::web::Event)>>)).unwrap(),
            });
            current.buff_new.push(quote! {
                    #clones
                    let #closure = Some(#closure_expr);
                    #name
                        .add_event_listener_with_callback(#event, yarte_wasm_app::JsCast::unchecked_ref(#closure.as_ref().unwrap().as_ref()))
                    .unwrap_throw();
                });
            current
                .path_events
                .push((name.clone(), current.steps.clone()));
            current.buff_hydrate.push(quote! {
                    #clones
                    let __closure__ = #closure_expr;
                    #dom.#name
                        .add_event_listener_with_callback(#event, yarte_wasm_app::JsCast::unchecked_ref(__closure__.as_ref()))
                    .unwrap_throw();
                    #dom.#closure.replace(__closure__);
                });
            current.buff_render.push((
                vars.clone(),
                quote! {
                    #dom.#name
                    .remove_event_listener_with_callback(
                        #event,
                        yarte_wasm_app::JsCast::unchecked_ref(#dom
                            .#closure
                            .as_ref()
                            .unwrap_throw()
                            .as_ref()),
                    )
                    .unwrap_throw();
                    #clones
                    #dom.#closure.replace(#closure_expr);
                },
            ));
            current
                .path_nodes
                .push((name.clone(), current.steps.clone()));

            // TODO: duplicated node
            current.black_box.push(BlackBox {
                doc: "Yarte Node element".to_string(),
                name,
                ty: parse2(quote!(yarte_wasm_app::web::Element)).unwrap(),
            });
        };
    }
}