rshtml_core 0.5.0

RsHtml: A Template Engine for Seamless HTML and Rust Integration.
Documentation
use crate::{compiler::Compiler, position::Position};
use anyhow::{Result, anyhow};
use proc_macro2::TokenStream;
use quote::quote;
use std::path::Path;
use syn::{Expr, parse_quote, parse_str, visit_mut::VisitMut};

pub struct ExprCompiler<'a>(&'a Vec<String>, bool);

impl<'a> ExprCompiler<'a> {
    pub fn compile(
        compiler: &mut Compiler,
        expr: String,
        is_escaped: bool,
        position: Position,
    ) -> Result<TokenStream> {
        let mut expression =
            parse_str::<Expr>(&expr).map_err(|err| anyhow!("Expression Parsing Error: {err}"))?;

        let component = compiler
            .components
            .get(&compiler.component_path)
            .ok_or(anyhow!("component not found"))?;

        let mut visitor = ExprCompiler(&component.fn_names, false);
        visitor.visit_expr_mut(&mut expression);
        let is_fn = visitor.1;

        let expr_ts = if is_fn {
            quote!(#expression;)
        } else {
            let file = compiler
                .files
                .iter()
                .last()
                .map(|x| x.0.as_path())
                .unwrap_or(Path::new("<unknown>"));

            let message = compiler.diagnostic.caution(
                file,
                &position,
                "attempt to use an expression that does not implement the Display trait.",
                &[],
                "this expression does not implement the Display trait.",
                expr.len(),
            );
            Self::escape_or_raw(quote!(#expression), is_escaped, &message)
        };

        let expr_ts = compiler.with_info(expr_ts, position, None);

        Ok(expr_ts)
    }

    fn escape_or_raw(expr_ts: TokenStream, is_escaped: bool, message: &str) -> TokenStream {
        if is_escaped {
            quote! { ::rshtml::Expr(&(#expr_ts)).render(&mut ::rshtml::EscapingWriter { inner: __f__ }, #message)?; }
        } else {
            quote! { ::rshtml::Expr(&(#expr_ts)).render(__f__, #message)?; }
        }
    }
}

impl<'a> VisitMut for ExprCompiler<'a> {
    fn visit_expr_mut(&mut self, node: &mut Expr) {
        if let Expr::Call(expr_call) = node
            && let Expr::Path(ref func_path) = *expr_call.func
            && func_path.path.segments.len() == 1
        {
            let ident = &func_path.path.segments[0].ident;

            if self.0.contains(&ident.to_string()) {
                let args = &expr_call.args;

                *node = parse_quote! {self.#ident(__f__, #args)?};
                self.1 = true;
            }
        }
    }
}