femstache 0.1.2

Embrace the feminine mustache to unlock your templating dreams! Or something, I don't know. :3
Documentation
use std::collections::BTreeMap;

use femstache_parser::nodes::{
    Control, Document, DocumentNode, DocumentNodes, Expr, Literal, Stache,
};

use crate::{
    env::TemplateEnv,
    error::{EvalError, EvalResult},
    input::TemplateInput,
    loader::{LoaderError, TemplateLoader},
    output::Output,
    value::Value,
};

pub struct TemplateRenderer<'i, I: TemplateInput> {
    env: TemplateEnv<'i, I>,
    blocks: BTreeMap<String, String>,
    loader: Option<TemplateLoader>,
}

impl<'i, I: TemplateInput> TemplateRenderer<'i, I> {
    pub fn new(
        env: TemplateEnv<'i, I>,
        blocks: BTreeMap<String, String>,
        loader: Option<TemplateLoader>,
    ) -> Self {
        Self {
            env,
            blocks,
            loader,
        }
    }

    pub fn render_document(&mut self, document: &Document, output: &mut Output) -> EvalResult<()> {
        if let Some(meta) = &document.meta {
            if let Some(inherit) = &meta.inherit {
                if let Some(loader) = self.loader.as_mut() {
                    let template = loader.load(&inherit.path.as_str())?;
                    self.env.push();
                    self.render_document_nodes(&document.nodes, &mut Output::Null)?;
                    self.env.pop();
                    self.env.push();
                    self.render_document(&template.doc, output)?;
                    self.env.pop();
                } else {
                    return Err(EvalError::Loader(LoaderError::NoLoader));
                }
            }
        }
        self.render_document_nodes(&document.nodes, output)?;
        Ok(())
    }

    pub fn render_document_nodes(
        &mut self,
        nodes: &DocumentNodes,
        output: &mut Output,
    ) -> EvalResult<()> {
        for node in &nodes.nodes {
            self.render_document_node(node, output)?;
        }
        Ok(())
    }

    pub fn render_document_node(
        &mut self,
        node: &DocumentNode,
        output: &mut Output,
    ) -> EvalResult<()> {
        match node {
            DocumentNode::Output(o) => {
                output.push(&o.data);
                Ok(())
            }
            DocumentNode::Stache(s) => self.render_stache(s, output),
            DocumentNode::Control(c) => self.render_control(c, output),
        }
    }

    pub fn render_stache(&mut self, stache: &Stache, output: &mut Output) -> EvalResult<()> {
        let val = self.eval_expr(&stache.expr)?;
        let s = val.as_str().ok_or_else(|| EvalError::InvalidType)?;
        output.push(s);
        Ok(())
    }

    pub fn render_control(&mut self, control: &Control, output: &mut Output) -> EvalResult<()> {
        match control {
            Control::IfControl(if_control) => {
                let cond = self.eval_expr(&if_control.open.cond)?;
                if cond.is_truthy() {
                    self.env.push();
                    self.render_document_nodes(&if_control.inner, output)?;
                    self.env.pop();
                }
            }
            Control::ForControl(for_control) => {
                let value = self.eval_expr(&for_control.open.expr)?;
                let list = value.into_list().ok_or_else(|| EvalError::InvalidType)?;
                for item in list {
                    self.env.push();
                    self.env.set(&for_control.open.var.data, item);
                    self.render_document_nodes(&for_control.inner, output)?;
                    self.env.pop();
                }
            }
            Control::BlockControl(block_control) => {
                self.env.push();
                if let Some(block) = self.blocks.get(&block_control.open.ident.data) {
                    output.push(&block);
                } else {
                    let key = block_control.open.ident.data.clone();
                    let mut block = Output::String(String::new());
                    self.env.push();
                    self.render_document_nodes(&block_control.inner, &mut block)?;
                    let block = block.into_inner();
                    output.push(&block);
                    self.blocks.insert(key, block);
                    self.env.pop();
                }
                self.env.pop();
            }
            Control::IncludeControl(include_control) => {
                if let Some(loader) = self.loader.as_mut() {
                    let template = loader.load(&include_control.path.as_str())?;
                    self.env.push();
                    self.render_document(&template.doc, output)?;
                    self.env.pop();
                } else {
                    return Err(EvalError::Loader(LoaderError::NoLoader));
                }
            }
            Control::LetControl(let_control) => {
                let value = self.eval_expr(&let_control.expr)?;
                self.env.set(&let_control.var.data, value);
            }
        }
        Ok(())
    }

    pub fn eval_expr(&mut self, expr: &Expr) -> EvalResult<Value> {
        match expr {
            Expr::Ident(ident) => self
                .env
                .get(&ident.data)
                .ok_or_else(|| EvalError::VariableNotFound(ident.clone())),
            Expr::Literal(literal) => Ok(match literal {
                Literal::Null(_) => Value::Null,
                Literal::True(_) => Value::Bool(true),
                Literal::False(_) => Value::Bool(false),
                Literal::String(s) => Value::String(s.as_str().to_owned()),
                Literal::List(l) => {
                    let mut items = Vec::new();
                    for item in &l.items {
                        let value = self.eval_expr(&item)?;
                        items.push(value);
                    }
                    Value::List(items)
                }
            }),
        }
    }
}