Keats / tera

A template engine for Rust based on Jinja2/Django
http://keats.github.io/tera/
MIT License
3.43k stars 279 forks source link

Macro definition doesn't work in simple template #644

Open mbme opened 3 years ago

mbme commented 3 years ago

Macro definition doesn't work in simple template, but works if template extends other template. Importing the macro from a separate template also works.

This works:

use tera::{Context, Tera};

fn main() {
    let mut tera = Tera::default();
    let context = Context::new();

    tera.add_raw_template("base", "{% block content %}{% endblock content %}")
        .unwrap();
    tera.add_raw_template(
        "test-macro-extends",
        "
{% extends \"base\" %}

{% macro hello_world() %}
hello world!
{% endmacro hello_world %}

{% block content %}
{{ self::hello_world() }}
{% endblock content %}
",
    )
    .unwrap();

    assert!(tera.render("test-macro-extends", &context).is_ok());
}

This doesn't work:

use tera::{Context, Tera};

fn main() {
    let mut tera = Tera::default();
    let context = Context::new();

    tera.add_raw_template(
        "test-macro",
        "
{% macro hello_world() %}
hello world!
{% endmacro hello_world %}

{{ self::hello_world() }}
",
    )
    .unwrap();

    assert!(tera.render("test-macro", &context).is_err());
    println!("{:#?}", tera.render("test-macro", &context));
}

Error:

Err(
    Error {
        kind: Msg(
            "Failed to render \'test-macro\'",
        ),
        source: Some(
            Error {
                kind: InvalidMacroDefinition(
                    "hello_world",
                ),
                source: None,
            },
        ),
    },
)
jmcph4 commented 1 year ago

I think I'm also encountering related behaviour, although my macro is more complex.

{% macro comment_tree(comment, depth) %}
    <p>{{ comment.author.username }} at {{ comment.created }} said:</p>
    <p>{{ comment.contents }}</p>
    <hr>

    {% for child in comment.children %}
        {{ self::comment_tree(child=child, depth=depth + 1) }}
    {% endfor %}
{% endmacro comment_tree %}

<p><a href="{{ link.url }}" target="_blank">{{ link.url }}</a> by {{ link.author.username }} at {{ link.created }}</p>
<hr>

{% for comment in link.comments %}
    {{ self::comment_tree(comment=comment, depth=0) }}
{% endfor %}

Using context derived from the following JSON blob:

{"id":1,"url":"https://example.com/","author":{"id":1,"username":"Coolio","is_admin":false},"created":"2022-12-29T23:14:03.368413763Z","comments":[{"id":1,"parent":null,"author":{"id":2,"username":"Yoyo","is_admin":false},"created":"2022-12-29T23:14:03.368425139Z","contents":"Wow, great link!","children":[{"id":2,"parent":null,"author":{"id":1,"username":"Coolio","is_admin":false},"created":"2022-12-29T23:14:03.368425686Z","contents":"Shut up","children":[]}]}]}

This is from within a Rocket application, but I've also tested it in the Playground and in a separate test harness:

use serde_json::*;
use tera::{Context, Tera};

const BLOB: &str = r#"
{"id":1,"url":"https://example.com/","author":{"id":1,"username":"Coolio","is_admin":false},"created":"2022-12-29T23:14:03.368413763Z","comments":[{"id":1,"parent":null,"author":{"id":2,"username":"Yoyo","is_admin":false},"created":"2022-12-29T23:14:03.368425139Z","contents":"Wow, great link!","children":[{"id":2,"parent":null,"author":{"id":1,"username":"Coolio","is_admin":false},"created":"2022-12-29T23:14:03.368425686Z","contents":"Shut up","children":[]}]}]}
"#;

fn main() {
    // Use globbing
    let mut tera = match Tera::parse("templates/*") {
        Ok(t) => t,
        Err(e) => {
            println!("Parsing error(s): {}", e);
            ::std::process::exit(1);
        }
    };
    tera.build_inheritance_chains().unwrap();

    let template_name: &str = "link.html.tera";
    let context: Context =
        Context::from_serialize(serde_json::from_str::<Value>(BLOB).unwrap()).unwrap();

    dbg!(&tera.check_macro_files());
    dbg!(&tera.render(template_name, &context));
}

After a brief session in rust-gdb, I've ended up here:

https://github.com/Keats/tera/blob/da358f389f589649b4b99bef70195a870f5f73ff/src/renderer/processor.rs#L1017-L1020