rshtml_core 0.4.0

RsHtml: A Template Engine for Seamless HTML and Rust Integration.
Documentation
use crate::{
    compiler::{Compiler, Component},
    node::{Function, Node},
    position::Position,
};
use anyhow::Result;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use std::path::PathBuf;
use syn::Ident;

pub struct TemplateCompiler;

impl TemplateCompiler {
    pub fn compile(
        compiler: &mut Compiler,
        path: PathBuf,
        name: String,
        fns: Vec<Function>,
        nodes: Vec<Node>,
        _position: Position,
    ) -> Result<TokenStream> {
        let fn_name = Ident::new(&compiler.generate_fn_name(&name), Span::call_site());

        let fn_call_ts = if compiler.is_root {
            compiler.is_root = false;
            let root_component_ts = quote! {self.#fn_name(__f__, |__f__: &mut dyn ::std::fmt::Write| -> ::std::fmt::Result {Ok(())})?;};

            Ok(quote! {#root_component_ts})
        } else {
            Ok(quote! {})
        };

        if !compiler.components.contains_key(&path) {
            let prev_component_path = compiler.component_path.to_owned();
            compiler.component_path = path.to_owned();

            compiler.components.insert(
                path.to_owned(),
                Component::new(
                    fn_name.to_owned(),
                    fns.iter().map(|f| f.name.to_owned()).collect(),
                ),
            );

            let mut token_stream = TokenStream::new();
            for node in nodes {
                let ts = compiler.compile(node)?;
                token_stream.extend(quote! {#ts});
            }

            let component_ts = token_stream.to_owned();

            if let Some(component_data) = compiler.components.get_mut(&path) {
                let struct_name = &compiler.struct_name;
                let (impl_generics, type_generics, where_clause) =
                    compiler.struct_generics.split_for_impl();

                let (fn_signs, fn_bodies): (Vec<&TokenStream>, Vec<&TokenStream>) =
                    component_data.fns.iter().map(|(k, v)| (k, v)).unzip();
                let args = component_data.params_to_ts()?;

                let component_fns = if component_data.fns.is_empty() {
                    quote! {}
                } else {
                    quote! {
                        trait __rshtml__fns {
                            #(#fn_signs)*
                        }
                        impl #impl_generics __rshtml__fns for #struct_name #type_generics #where_clause {
                            #(#fn_bodies)*
                        }
                    }
                };

                let component_ts = quote! {
                fn #fn_name(&self,
                    __f__: &mut dyn ::std::fmt::Write,
                    child_content: impl Fn(&mut dyn ::std::fmt::Write) -> ::std::fmt::Result,
                    #args) -> ::std::fmt::Result {#component_fns #component_ts  Ok(())}
                };

                component_data.token_stream = component_ts.to_owned();
            }

            compiler.component_path = prev_component_path;
        }

        fn_call_ts
    }
}