It would be convenient if FlashMessage implemented Serialize, so I could stick it in my context struct that I pass to a template.
What would it Serialize to? In general, I'd like to keep core as dependence free as possible. It's not difficult to call name and msg on the FlashMessage, so unless there a pretty convincing argument for doing this, I don't think we should do this.
It would serialize exactly the same way that
#[derive(Serialize)]
struct Flash<'a> {
name: &'a str,
msg: &'a str
}
would. Calling name and msg are in fact very annoying when trying to stuff them into a context struct, because it means I now have to have 2 Option<&'a str> fields on my struct, instead of one Option<FlashMessage> field, which bugs me because it now becomes possible to evaluate the template with only one of the fields filled in.
I've ended up writing my own struct identical to what's above, and having a fn new(flash: FlashMessage) -> Self to convert the FlashMessage into it, so that way I can have
#[derive(Serialize)]
struct Context<'a> {
flash: Option<FlashMsg<'a>>,
title: &'a str,
author: &'a str,
...
}
Why not just map to a tuple?
msg.map(|m| (m.name(), m.msg()))
I'd be happy to add a name_msg() method to FlashMessage that returns that tuple.
Tuples don't have named fields. I'm not even sure offhand what I would expect the resulting Tera::Context to have for that field, maybe an array? It certainly can't turn into the equivalent of { name: String, msg: String } since there's no field names.
The idea here is in my templates I can say
{% if flash %}
<div id="flash" class="flash_{{ flash.name }}">{{ flash.msg }}</div>
{% endif %}
Yeah, it'd be an array. You'd just index it like a tuple using .0 and .1.
An alternative is that we can add a method that returns a HashMap with msg and value fields, but that's an entire, needless allocation.
Yeah, that's why I'd rather just have FlashMessage implement Serialize, because I can then just drop it into my struct as-is and there'd be no unnecessary allocations.
Hmm. Maybe we should move Flash and FlashMessage into contrib so that we can enable this functionality. You don't like the .name_msg() method idea?
I don't want a tuple, because I don't want to have to use {{ flash.0 }} and {{ flash.1 }} in my templates (anyone reading the template would have no idea what the meaning of that is).
I think wanting FlashMessage to be serializable sounds reasonable. Would certainly make accessing it from a template easier.
I've been finding myself wanting to do this:
#[get("/login")]
fn login(flash: Option<FlashMessage>) -> Template {
let context = json!({
// other things here
"flash": flash,
});
Template::render("login", &context)
}
Which would be super useful. Maybe rocket_contrib could have a version of flash that is serializable?
Mind you, I currently use this, which is simple but does the trick:
use rocket::request::{self, Request, FromRequest};
use rocket::response::{Flash};
use rocket::Outcome::{Success, Failure};
use rocket::http::Status;
#[derive(Debug, Serialize)]
pub struct FlashMessage {
pub name: String,
pub message: String,
}
impl From<Flash<()>> for FlashMessage {
fn from(flash: Flash<()>) -> Self {
FlashMessage {name: flash.name().into(), message: flash.msg().into()}
}
}
impl<'a, 'r> FromRequest<'a, 'r> for FlashMessage {
type Error = ();
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
if let Success(flash) = Flash::from_request(request) {
Success(flash.into())
}
else {
Failure((Status::NotFound, ()))
}
}
}
And then using the flash with this:
{% if flash %}
<div class="alert alert-dismissible rounded-bottom fade show alert-{{flash.name | replace(from="error", to="danger")}}" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<strong class="pr-3">
{% if flash.name == "error" %}
<span class="fa fa-times-circle fa-lg"></span>
{% elif flash.name == "warning" %}
<span class="fa fa-exclamation-circle fa-lg"></span>
{% elif flash.name == "success" %}
<span class="fa fa-check-circle fa-lg"></span>
{% endif %}
</strong>{{flash.message}}
</div>
{% endif %}
Most helpful comment
I don't want a tuple, because I don't want to have to use
{{ flash.0 }}and{{ flash.1 }}in my templates (anyone reading the template would have no idea what the meaning of that is).