I'm trying to make a recursive macro in an askama template, so that I can render a filetree via a recursive walk.
I have a FileTree data type like this:
pub struct FileTree {
pub name: String,
pub path: PathBuf,
pub is_dir: bool,
pub children: Vec<FileTree>,
}
In Askama, I have a template like this:
{% macro render_node(node) %}
{% if node.is_dir %}
{{ node.name }}
{% for child in node.children %}
{{ call render_node(child) }}
{% endfor %}
{% else %}
{% include "file.html" %}
{% endif %}`
{% endmacro %}
{% call render_node(filetree) %}
But at compile time, I get the error:
error: unknown node `render_node`
--> templates/macros.html:34:28
"render_node(child) }}\n {% endfor %}\n </ol>\n </d"...
Which seems to tell me that the macro cannot call itself from within itself. Is there a way to do this in askama? Or a different syntax I need?
Based on my current reading of askama, I am not sure if this type of recursion is possible (see: Template syntax - Askama), perhaps due to the way it compiles templates, but I am not sure.
I also tried doing a recursive include instead of a recursive macro, which lead to compilation crashing.
Instead, I have found a working solution I am happy with that does the recursion in Rust, outside of askama, but calling .render() on the templates at the right time.
Something like:
filetree.rs
use askama::Template;
#[derive(Template)]
#[template(path = "file.html")]
pub struct FileTemplate {
pub name: String,
}
#[derive(Template)]
#[template(path = "directory.html")]
pub struct DirectoryTemplate {
pub name: String,
pub children_html: String,
}
pub fn render_node(node: &FileTree) -> askama::Result<String> {
// case for files
if !node.is_dir {
let template = FileTemplate {
name: node.name.to_string(),
};
template.render()
}
// case for directories
else {
let mut children_html = String::new();
for child in &node.children {
children_html.push_str(&render_node(child)?);
}
let template = DirectoryTemplate {
name: node.name.to_string(),
children_html: children_html,
};
template.render()
}
}
directory.html
<li class="directory">
<div class="dir-name">{{ name }}</div>
<ul>
{{ children_html | safe }}
</ul>
</li>
file.html
<li class="file">
{{ name }}
</li>
And then one top-level template, which receives a String of the output of render_node at the root of the filetree I want to render.
page.html
<ul class="tree">
{{ tree_html | safe }}
</ul>