askama icon indicating copy to clipboard operation
askama copied to clipboard

First thing that's not None or empty

Open alper opened this issue 1 year ago • 3 comments

Is there a way to do something like: {{ venue.name or venue.fallback_name }}

So take the first thing that's not None or empty and render that?

alper avatar Apr 23 '23 09:04 alper

Currently I have to do this like so, which is verbose:

{% if !venue.name.is_empty() %}
    {{ venue.name }}
{% else %}
    {{ venue.google_name }}
{% endif %}

alper avatar Apr 23 '23 09:04 alper

Assuming that name is Option<T> (where T is e.g. String) and fallback_name then is String. Then you can utilize Option's methods, such as or() and unwrap_or().

So say we have the following struct:

#[derive(Template)]
struct Page {
    a: Option<String>,
    b: Option<String>,
    default: String,
}

Then to be able to render a otherwise default, you can do:

{{ a.as_deref().unwrap_or(default.as_str()) }}

If you want to render a otherwise b otherwise default, then you can do:

{{ a.as_deref().or(b.as_deref()).unwrap_or(default.as_str()) }}

Complete Example:

#[derive(Template)]
#[template(
    source = "{{ a.as_deref().or(b.as_deref()).unwrap_or(default.as_str()) }}",
    ext = "html"
)]
struct Page {
    a: Option<String>,
    b: Option<String>,
    default: String,
}

let pages = [
    Page {
        a: Some("a".into()),
        b: None,
        default: "default".into(),
    },
    Page {
        a: None,
        b: Some("b".into()),
        default: "default".into(),
    },
    Page {
        a: Some("a".into()),
        b: Some("b".into()),
        default: "default".into(),
    },
    Page {
        a: None,
        b: None,
        default: "default".into(),
    },
];

for page in pages {
    println!("{:?}", page.render().unwrap());
}

Which would output:

"a"
"b"
"a"
"default"

vallentin avatar Apr 28 '23 15:04 vallentin

An Option can also be used as a single element iterator, and might come in handy with for-else.

Kijewski avatar Apr 28 '23 16:04 Kijewski