go_html_template 0.2.1

A pure Rust crate with Go html/template-like API and behavior
Documentation
use go_html_template::Template;
use serde_json::json;
use std::time::Instant;

fn bench<F: FnMut()>(name: &str, loops: usize, mut f: F) {
    let mut total_us: u128 = 0;
    for _ in 0..loops {
        let start = Instant::now();
        f();
        total_us += start.elapsed().as_micros();
    }
    println!(
        "{name:28} loops={loops:4} avg_us={}",
        total_us / loops as u128
    );
}

fn main() {
    let loops_parse_small = 500;
    let loops_parse_large = 60;
    let loops_exec = 60;

    bench("parse_small", loops_parse_small, || {
        let _ = Template::new("p")
            .parse("<p>{{.Name}}</p>")
            .expect("parse should succeed");
    });

    let mut large_expr = String::new();
    for _ in 0..20_000 {
        large_expr.push_str("{{.X}}");
    }
    bench("parse_expr_20k", loops_parse_large, || {
        let _ = Template::new("p")
            .parse(&large_expr)
            .expect("parse should succeed");
    });

    let static_200k = "<ul>".to_string() + &"<li>x</li>".repeat(20_000) + "</ul>";
    let tmpl_text = Template::new("t")
        .parse(&static_200k)
        .expect("parse should succeed");
    let input_empty = json!({});
    bench("exec_text_200k", loops_exec, || {
        let _ = tmpl_text
            .execute_to_string(&input_empty)
            .expect("execute should succeed");
    });

    let mut expr_20k = String::new();
    for _ in 0..20_000 {
        expr_20k.push_str("{{.X}}");
    }
    let tmpl_expr = Template::new("t")
        .parse(&expr_20k)
        .expect("parse should succeed");
    let input_x = json!({"X": "abc"});
    bench("exec_expr_20k", loops_exec, || {
        let _ = tmpl_expr
            .execute_to_string(&input_x)
            .expect("execute should succeed");
    });

    let mut deep_20k = String::new();
    for _ in 0..20_000 {
        deep_20k.push_str("{{.A.B.C.D.E}}\n");
    }
    let tmpl_deep = Template::new("t")
        .parse(&deep_20k)
        .expect("parse should succeed");
    let input_deep = json!({"A": {"B": {"C": {"D": {"E": "abc"}}}}});
    bench("exec_deep_path_20k", loops_exec, || {
        let _ = tmpl_deep
            .execute_to_string(&input_deep)
            .expect("execute should succeed");
    });

    let tmpl_range_no_vars = Template::new("t")
        .parse("<ul>{{range .Items}}<li>x</li>{{end}}</ul>")
        .expect("parse should succeed");
    let input_items = json!({"Items": (0..20_000).collect::<Vec<_>>()});
    bench("exec_range_no_vars", loops_exec, || {
        let _ = tmpl_range_no_vars
            .execute_to_string(&input_items)
            .expect("execute should succeed");
    });

    let tmpl_range_vars = Template::new("t")
        .parse("{{range $i, $v := .Items}}{{$i}}:{{$v}};{{end}}")
        .expect("parse should succeed");
    bench("exec_range_with_vars", loops_exec, || {
        let _ = tmpl_range_vars
            .execute_to_string(&input_items)
            .expect("execute should succeed");
    });

    let mut func_20k = String::new();
    for _ in 0..20_000 {
        func_20k.push_str("{{print .X}}");
    }
    let tmpl_func = Template::new("t")
        .parse(&func_20k)
        .expect("parse should succeed");
    bench("exec_builtin_func_20k", loops_exec, || {
        let _ = tmpl_func
            .execute_to_string(&input_x)
            .expect("execute should succeed");
    });

    bench("serde_to_value_only", loops_exec * 20, || {
        let _ = serde_json::to_value(&input_items).expect("to_value should succeed");
    });
}