/// MoonTemplate — A Handlebars-style template engine for MoonBit.
///
/// # Quick Start
///
/// ```moonbit
/// let ctx = [("name", "World")]
/// let html = @moontemplate.render_template("Hello {{ name }}!", ctx)
/// // html == "Hello World!"
/// ```
///
/// # Features
/// - Variable interpolation: {{ name }}
/// - Raw/unescaped output: {{{ html }}}
/// - Conditionals: {{#if condition}} ... {{/if}}
/// - If/Else: {{#if condition}} ... {{else}} ... {{/if}}
/// - Loops: {{#each items}} ... {{/each}}
/// - Partials: {{> partial_name}}
/// - Helpers: custom transformation functions
/// - Automatic HTML escaping
/// - Error diagnostics with line/column info
///|
/// Render a template string with context pairs.
pub fn render_template(
template : String,
ctx : Array[(String, String)],
) -> String {
let tokens = @lexer.tokenize(template)
let ast = @parser.parse(tokens)
@render.render(ast, ctx)
}
///|
/// Compile a template string into an AST for reuse.
pub fn compile(template : String) -> @types.Node {
let tokens = @lexer.tokenize(template)
@parser.parse(tokens)
}
///|
/// Render a compiled AST with context.
pub fn render(ast : @types.Node, ctx : Array[(String, String)]) -> String {
@render.render(ast, ctx)
}
///|
/// Render a template with enhanced context, partials, and helpers.
pub fn render_enhanced(
template : String,
ctx : Array[(String, String)],
partials : Array[@types.Partial],
helpers : Array[@types.Helper],
) -> String {
let tokens = @lexer.tokenize(template)
let ast = @parser.parse(tokens)
@render.render_enhanced(ast, ctx, partials, helpers)
}
///|
/// Escape HTML special characters. Public for convenience.
pub fn escape_html(text : String) -> String {
let mut result = ""
let mut i = 0
while i < text.length() {
match text[i] {
'&' => result = result + "&"
'<' => result = result + "<"
'>' => result = result + ">"
'"' => result = result + """
'\'' => result = result + "'"
'`' => result = result + "`"
_ => result = result + text[i:i + 1].to_owned()
}
i = i + 1
}
result
}
///|
/// Find a partial by name in the partials array.
pub fn find_partial(name : String, partials : Array[@types.Partial]) -> String {
for partial in partials {
if partial.name == name {
return partial.template
}
}
""
}
///|
/// Apply a named helper to a value.
pub fn apply_helper(
name : String,
value : String,
helpers : Array[@types.Helper],
) -> String? {
for helper in helpers {
if helper.name == name {
let result = (helper.func)(value)
return Some(result)
}
}
None
}