djc / askama

Type-safe, compiled Jinja-like templates for Rust
Apache License 2.0
3.25k stars 213 forks source link

Template Rendering of a Result<T + Display, E + Display> Cleanly #1059

Open jaw-sh opened 1 month ago

jaw-sh commented 1 month ago

I have a template that I call a View. It has implementations which return templates-in-templates with derived values. Sometimes these can fail and return results. These results need to be handled inside the template, even though both arms of Result<> implement Display.

/// View for editing a single post.
#[derive(Template)]
#[template(path = "post/edit.html")]
pub struct PostEditView {
    pub context: Context,
    pub attachment_collection: AttachmentCollection,
    pub post_collection: PostCollection,
    pub post_form: PostForm,
}

impl PostEditView {
    pub fn post_edit_template(&self) -> PostFormPartial {
        PostFormPartial {
            attachment_collates: Default::default(),
            post_collate: None,
            post_form: &self.post_form,
            thread_collate: None,
        }
    }

    pub fn post_template(&self) -> Result<impl Template + '_, Error> {
        Ok(PostPartial {
            attachment_collates: self.attachment_collection.collates(),
            post_collate: self.post_collection.collate_single()?,
            post_position: None,
        })
    }
}

My template could look like this:

{% block content %}
{{ self.post_template() }}
{{ self.post_edit_template() }}
{% endblock %}

Instead, it must look like this:

{% block content %}

{% self.post_template() }}
{% when Ok(post) %}
{{ post }}
{% when Err(e) %}
{{ e }}
{% endmatch %}

{{ self.post_edit_template() }}

{% endblock %}

I don't like this. It would be super cool if there was a way to simply {{ result }} and render either branch automatically.

Kijewski commented 1 month ago

I don't like this. It would be super cool if there was a way to simply {{ result }} and render either branch automatically.

Have a look how to implement custom filters. Adding your own |either_result filter would be simple enough.

I wouldn't like askama to branch both cases automagically, because I would most like wrap the error message in e.g. <div class="error">. If the template engine makes it harder to find such cases, it would be a disadvantage to me.

jaw-sh commented 1 month ago

Have a look how to implement custom filters. Adding your own |either_result filter would be simple enough.

I did attempt this but had compiler errors regarding sized types and another anomalous issues I couldn't resolve myself. I would accept this as a solution if I could manage it.

djc commented 1 month ago

Have a look how to implement custom filters. Adding your own |either_result filter would be simple enough.

I did attempt this but had compiler errors regarding sized types and another anomalous issues I couldn't resolve myself. I would accept this as a solution if I could manage it.

A filter should definitely be possible. Do you have the exact error message?

I don't think there's a general solution here because a blanket impl for Display for Result<T, E> just doesn't make sense even if T and E both implement Display. A Display impl might be possible (coherence willing) for specific T/E combinations, of course.

A filter or macro definitely feels like the straightforward option here.