femstache 0.1.6

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

use femstache_eval::{TemplateEnv, TemplateInput};
use femstache_loader::{TemplateLoader, TemplateSource};
use femstache_parser::Template;
use femstache_types::{TyContext, TyError};

use crate::{
    FemstacheError, FemstacheResult,
    output::{EscapeMode, Output},
    renderer::TemplateRenderer,
};

/// Encapsulates all of the template loading and rendering logic; this is most
/// likely what you want.
#[derive(Debug)]
pub struct Femstache {
    loader: TemplateLoader,
    escape: EscapeMode,
}

impl Femstache {
    /// Create a [Femstache] instance from a [TemplateSource] and [EscapeMode]
    pub fn new(source: TemplateSource, escape: EscapeMode) -> Self {
        let loader = TemplateLoader::new(source);
        Self { loader, escape }
    }

    /// Render the template at `path` in the store, with `inputs` as the inputs
    pub fn render(&self, path: &str, inputs: &impl TemplateInput) -> FemstacheResult<String> {
        let template = self.loader.load(path)?;
        let mut ctx = TyContext::new(self.loader.clone());
        ctx.infer_document(template.document())?;
        for (name, ty_id) in ctx.globals() {
            // TODO: handle optional
            let Some(value) = inputs.get(name) else {
                // TODO: better errors
                return Err(FemstacheError::TypeError(TyError::Conflicts(Vec::new())));
            };
            if !value.conforms_to(&ctx, *ty_id) {
                // TODO: better errors
                return Err(FemstacheError::TypeError(TyError::Conflicts(Vec::new())));
            }
        }
        let env = TemplateEnv::new(inputs);
        let mut renderer = TemplateRenderer::new(env, BTreeMap::new(), Some(self.loader.clone()));
        let mut output = Output::string(self.escape);
        renderer.render_document(template.document(), &mut output)?;
        Ok(output.into_inner())
    }
}

/// Render a template using the specified input
///
/// ```rust
/// # use std::collections::BTreeMap;
/// # use femstache::Template;
/// let tpl = Template::parse("hello, {{place}}! :3c").unwrap();
///
/// let mut map = BTreeMap::new();
/// map.insert("place", "delicious world");
///
/// assert_eq!(femstache::render(&tpl, &map).unwrap(), "hello, delicious world! :3c");
/// ```
pub fn render(template: &Template, inputs: &impl TemplateInput) -> FemstacheResult<String> {
    let mut ctx = TyContext::new(TemplateLoader::new(TemplateSource::none()));
    ctx.infer_document(template.document()).unwrap();
    for (name, ty_id) in ctx.globals() {
        // TODO: handle optional
        let Some(value) = inputs.get(name) else {
            // TODO: better errors
            return Err(FemstacheError::TypeError(TyError::Conflicts(Vec::new())));
        };
        if !value.conforms_to(&ctx, *ty_id) {
            // TODO: better errors
            return Err(FemstacheError::TypeError(TyError::Conflicts(Vec::new())));
        }
    }
    let env = TemplateEnv::new(inputs);
    // TODO: it should be possible to set this
    let blocks = BTreeMap::new();
    let mut renderer = TemplateRenderer::new(env, blocks, None);
    let mut output = Output::string(EscapeMode::None);
    renderer.render_document(template.document(), &mut output)?;
    Ok(output.into_inner())
}