Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature request] Turbo-fish syntax in custom filters #743

Closed
saona-raimundo opened this issue Nov 10, 2022 · 3 comments
Closed

[Feature request] Turbo-fish syntax in custom filters #743

saona-raimundo opened this issue Nov 10, 2022 · 3 comments

Comments

@saona-raimundo
Copy link
Contributor

I have a custom filter that is naturally parametrized by a usize and I was hoping to implement it in a turbo-fish manner with a const parameter.

My custom filter indents the string by prefixing a string, a number of times, to each line. The code is the following.

    pub fn indent_by<const N: usize>(s: &str) -> ::askama::Result<String> {
    	let mut replace = "\n".to_string();
    	replace.extend(["    "; N]);
        Ok(s.replace("\n", &replace))
    }

I would like to call this filter by writing the filter indent_by::<2> in the Askama template (which is a stateless function).

Use-case

I am translating the Rule-Based approach for transformations in XLST to Rust. The idea is to code a function that takes an (XML) input and outputs another text format. Sometimes, this can be done by delegating the processing to the children. In light of #575, I implemented it as you suggested: having fields that are templates themselves. the result makes a lot of sense from the Rule-Based approach.

By using {{ child_template.render()?|safe }}, I can locate the children result in a template, but to have full control of the output, I need to indent it like this {{ child_template.render()?|indent_by::<X>|safe }}.

I could indent as a post-processing, but I lose a lot of control over the output.

Example

Note: I tried to come up with a minimal example, so I had to strip a few things.
We want to display a poem, written in XML, in HTML. The input is

<poem>
   <author>Rupert Brooke</author>
   <title>Song</title>
</poem>

The desired output is

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>
        Song
    </title>
</head>
<body>
    <article>
        <h1>
            Song
        </h1>
        <address>
            Rupert Brooke
        </address>
    </article>
</body>
</html>

My approach is to have 3 templates: File, Title, and Author, where File delegates to the others to achieve a Rule-Based approach. These are the templates.

{# file.html.askama -#}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>
        {{ title.render()?|indent_by::<2>|safe }} {#- delegation #}
    </title>
</head>
<body>
    <article>
        <h1 class="display">
            {{ title.render()?|indent_by::<3>|safe }} {#- delegation #}
        </h1>
        {{ author.render()?|indent_by::<2>|safe }} {#- delegation #}
    </article>
</body>
</html>
{# title.html.askama -#}

{{ text }}
{# author.html.askama -#}

<address class="display">
    {{ text }}
</address>

In the Rust side, I would have the following code.

#[derive(Template)]
#[template(path = "file.html.askama", ext = "html", escape = "html")]
struct File<'a> {
    title: Title<'a>,
    author: Author<'a>,
}
#[derive(Template)]
#[template(path = "title.html.askama", ext = "html", escape = "html")]
struct Title<'a> {
    text: &'a str,
}
#[derive(Template)]
#[template(path = "author.html.askama", ext = "html", escape = "html")]
struct Author<'a> {
    text: &'a str,
}
@djc
Copy link
Collaborator

djc commented Nov 10, 2022

I think this would add too much complexity in the template language. But you can just pass the value as an additional argument, right?

    pub fn indent_by(s: &str, n: usize) -> ::askama::Result<String> {
        let mut replace = String::with_capacity(1 + n * 4);
        replace.push('\n');
    	replace.extend(0..n.iter().map("    "));
        Ok(s.replace("\n", &replace))
    }

@saona-raimundo
Copy link
Contributor Author

Okay! Thanks for the answer :)

Yes! your solution totally works, sorry for not seeing it before. Also, there is already a indent built-in filter.

Would you consider expanding the Custom filters section of the book to indicate that filters can take extra arguments after the &str? (I can do a first draft if you want)

@djc
Copy link
Collaborator

djc commented Nov 10, 2022

Of course, a PR would be great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants