vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
use std::collections::HashMap;
use std::fmt;

fn render_inner(src: &str, ctx: &TemplateContext, out: &mut String) -> Result<(), TemplateError> {
    let mut i = 0;
    while i < src.len() {
        match src[i..].find('{') {
            Some(pos) => {
                out.push_str(&src[i..i + pos]);
                i += pos;

                let end = src[i..]
                    .find('}')
                    .ok_or(TemplateError::MalformedDirective)?;
                let directive = &src[i + 1..i + end];
                i += end + 1;

                if directive.starts_with("list:") {
                    let name = &directive[5..];
                    let body_end = find_block_end(&src[i..], "{list:", "{/list}")?;
                    let body = &src[i..i + body_end];
                    i += body_end + "{/list}".len();

                    let items = ctx
                        .lists
                        .get(name)
                        .ok_or_else(|| TemplateError::UnknownField(name.to_string()))?;
                    for item in items {
                        render_inner(body, item, out)?;
                    }
                } else if directive.starts_with("if:") {
                    let name = &directive[3..];
                    let body_end = find_block_end(&src[i..], "{if:", "{/if}")?;
                    let body = &src[i..i + body_end];
                    i += body_end + "{/if}".len();

                    if ctx.conditionals.get(name).copied().unwrap_or(false) {
                        render_inner(body, ctx, out)?;
                    }
                } else if directive.starts_with("include:") {
                    let name = &directive[8..];
                    let included = resolve_include(name)
                        .ok_or_else(|| TemplateError::IncludeNotFound(name.to_string()))?;
                    render_inner(included, ctx, out)?;
                } else {
                    let val = ctx
                        .scalars
                        .get(directive)
                        .ok_or_else(|| TemplateError::UnknownField(directive.to_string()))?;
                    out.push_str(val);
                }
            }
            None => {
                out.push_str(&src[i..]);
                break;
            }
        }
    }
    Ok(())
}