mod component;
mod extends_directive;
mod inner_text;
mod match_expr;
mod raw;
mod render_body;
mod render_directive;
mod rust_block;
mod rust_expr;
mod rust_expr_paren;
mod rust_expr_simple;
mod section_block;
mod section_directive;
mod text;
mod use_directive;
use crate::Node;
use crate::compiler::component::ComponentCompiler;
use crate::compiler::extends_directive::ExtendsDirectiveCompiler;
use crate::compiler::inner_text::InnerTextCompiler;
use crate::compiler::match_expr::MatchExprCompiler;
use crate::compiler::raw::RawCompiler;
use crate::compiler::render_body::RenderBodyCompiler;
use crate::compiler::render_directive::RenderDirectiveCompiler;
use crate::compiler::rust_block::RustBlockCompiler;
use crate::compiler::rust_expr::RustExprCompiler;
use crate::compiler::rust_expr_paren::RustExprParenCompiler;
use crate::compiler::rust_expr_simple::RustExprSimpleCompiler;
use crate::compiler::section_block::SectionBlockCompiler;
use crate::compiler::section_directive::SectionDirectiveCompiler;
use crate::compiler::text::TextCompiler;
use crate::compiler::use_directive::UseDirectiveCompiler;
use crate::position::Position;
use anyhow::Result;
use proc_macro2::TokenStream;
use quote::quote;
use std::collections::HashMap;
use std::path::PathBuf;
pub struct Compiler {
use_directives: Vec<(String, PathBuf)>,
components: HashMap<String, Node>,
layout_directive: PathBuf,
pub layout: Option<Node>,
sections: HashMap<String, TokenStream>,
pub section_body: Option<TokenStream>,
pub text_size: usize,
pub files: Vec<(String, Position)>,
}
impl Compiler {
pub fn new() -> Self {
Compiler {
use_directives: Vec::new(),
components: HashMap::new(),
layout_directive: PathBuf::new(),
layout: None,
sections: HashMap::new(),
section_body: None,
text_size: 0,
files: Vec::new(),
}
}
pub fn compile(&mut self, node: &Node) -> Result<TokenStream> {
match node {
Node::Template(file, nodes, position) => {
if !file.is_empty() {
self.files.push((file.clone(), position.clone()));
}
let mut token_stream = TokenStream::new();
for node in nodes {
let ts = self.compile(node)?;
token_stream.extend(quote! {#ts});
}
if !file.is_empty() {
self.files.pop();
}
Ok(token_stream)
}
Node::Text(text) => TextCompiler::compile(self, text),
Node::InnerText(inner_text) => InnerTextCompiler::compile(self, inner_text),
Node::Comment(_) => Ok(quote! {}),
Node::ExtendsDirective(path, layout) => {
ExtendsDirectiveCompiler::compile(self, path, layout)
}
Node::RenderDirective(name) => RenderDirectiveCompiler::compile(self, name),
Node::RustBlock(content, position) => {
RustBlockCompiler::compile(self, content, position)
}
Node::RustExprSimple(expr, is_escaped, position) => {
RustExprSimpleCompiler::compile(self, expr, is_escaped, position)
}
Node::RustExprParen(expr, is_escaped, position) => {
RustExprParenCompiler::compile(self, expr, is_escaped, position)
}
Node::MatchExpr(head, arms, position) => {
MatchExprCompiler::compile(self, head, arms, position)
}
Node::RustExpr(exprs, position) => RustExprCompiler::compile(self, exprs, position),
Node::SectionDirective(name, content, position) => {
SectionDirectiveCompiler::compile(self, name, content, position)
}
Node::SectionBlock(name, content) => SectionBlockCompiler::compile(self, name, content),
Node::RenderBody => RenderBodyCompiler::compile(self),
Node::Component(name, parameters, body, position) => {
ComponentCompiler::compile(self, name, parameters, body, position)
}
Node::ChildContent => Ok(quote! {child_content(__f__)?;}),
Node::Raw(body) => RawCompiler::compile(self, body),
Node::UseDirective(name, path, component) => {
UseDirectiveCompiler::compile(self, name, path, component)
}
Node::ContinueDirective => Ok(quote! {continue;}),
Node::BreakDirective => Ok(quote! {break;}),
}
}
pub fn section_names(&self) -> TokenStream {
let mut token_stream = TokenStream::new();
self.sections
.keys()
.for_each(|x| token_stream.extend(quote! {#x,}));
quote! {[#token_stream]}
}
fn escape_or_raw(&self, expr_ts: TokenStream, is_escaped: &bool) -> TokenStream {
if *is_escaped {
quote! {write!(rshtml::EscapingWriter { inner: __f__ }, "{}", &(#expr_ts))?;}
} else {
quote! {write!(__f__, "{}", #expr_ts)?;}
}
}
fn with_info(&self, expr_ts: TokenStream, position: &Position) -> TokenStream {
if cfg!(debug_assertions) {
let positions = self
.files
.iter()
.skip(1)
.map(|(_, pos)| pos)
.chain(std::iter::once(position));
let mappings: Vec<String> = self
.files
.iter()
.zip(positions)
.map(|((file, _), pos)| pos.as_info(file))
.collect();
let mapping = mappings.join(" > ");
if expr_ts.is_empty() {
quote! {#mapping;}
} else {
quote! {{#mapping;#expr_ts}}
}
} else {
expr_ts
}
}
}