This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
Support extracting messages in one pass? #11
Comments
I certainly think adding Regarding |
Hmm, can you expand more on this? I feel like the only use case where you'd need to consume it is for "last-mile" rendering (e.g, right now if you wanted to pass it to an Askama rendering context, you can't |
A couple other comments that I've run up against after an hour or so of using this lib: Is there any opposition to having mutable setters ( On the |
You mean such that
I'm not sure I'm exactly following, but do you mean if the messages field is empty you'd want to iterate through the pending_messages field transparently? Maybe you mean something else? |
Ah, sorry - looks like I braindumped too late at night. Lemme back up and break down these two last points. For context, in my application, I define my handlers as the following: pub async fn my_handler(mut request: RequestData<T>) -> crate::Result<Response>;
Given the above request.messages.error("Oh no, an error"); The issue here is that This can be worked around by doing something with Maybe methods like the following could be added? impl Messages {
pub fn add_error(&mut self, message: impl Into<String>) { ... }
}
messages.add_error("Oh no, an error"); The second issue I noted is the separation of pub async fn update_password(
mut request: RequestData<T>,
Form(form): Form<MyForm>
) -> crate::Result<Response> {
if let Err(e) = my_validation_method(&form) {
// This flash message will not render in the template, because it's added to
// pending_messages and not moved into messages (which iter() pulls from)
// until the request lifecycle is finished. The render to string technically happens
// here though.
request.messages.error("Internal error flash message");
return render(MyTemplate {
request,
form
});
}
// This flash message *will* render, because the redirect goes through the full request lifecycle
// and rehydrates the session after saving
request.messages.info("Great success");
redirect(StatusCode::SEE_OTHER, "/my/url/")
} In the case of this error, I want to re-render the form (with anything they modified on the form so far) and display an error to the user. It's unfortunately not possible as To work around this, I currently just use a local fork that has the following method added: /// Drains the *entire* messages stack.
pub fn drain(&self) -> Vec<Message> {
let mut data = self.data.lock();
let mut messages = vec![];
for queue in [
std::mem::take(&mut data.messages),
std::mem::take(&mut data.pending_messages)
] {
for message in queue.into_iter() {
messages.push(message);
}
}
if !self.is_modified() {
self.is_modified.store(true, atomic::Ordering::Release);
}
messages
} Then in my template, I can now just do: <section id="messages">
{% for message in request.messages.drain().into_iter() %}
<div class="msg msg-{{ message.level }}">{{ message }}</div>
{% endfor %}
</section> Hopefully that breaks down better what I was spewing late at night, but let me know if I can illustrate further - they're not full code samples but I think they should get the gist across. Also, I just want to be clear - I think your work is great and I 100% don't want to come across as trashing it or anything, these are mostly aiming to be feedback that might help a wider set of use-cases. :) [1] I'll refrain from ranting about why the extractor pattern that Rust frameworks tend to use isn't always great |
Why can't you do: |
There might be something there, but I'd need to revert my code back to confirm - but for what it's worth, I did experiment with a method on impl<T> RequestData<T> {
pub fn info(&mut self, message: impl Into<String>) {
self.messages = self.messages.info(message);
}
} ...which created the following error:
It could be worked around by the following, which I alluded to in my last comment regarding impl<T> RequestData<T> {
pub fn info(&mut self, message: impl Into<String>) {
self.messages = self.messages.clone().info(message);
}
} Personally speaking, I also am just not a fan of needing to pepper |
The idea with taking self and returning self is that you rebind the variable. The obvious thing that enables is a fluent call chain but less obviously you're meant to rebind in cases where you can't use just the fluent API. Regarding the second question, we have similar code in the axum-login SQLite example, but by using two HTTP verbs there's no issue with setting and reading the messages: GET renders the form, POST handles the form business logic (and e.g. error messages). The POST redirects via 303 See Other to the same path and the user agent re-renders the page with the messages. You can try this for yourself using the axum-login SQLite example and a bad password. Just to be clear here, I'm not opposed to extending this crate: I would like to continue to expand it in ways that help folks make use of it. I also want to make sure we're building around clear problems and use cases that don't have reasonable solutions. I think we should at least add |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
(I'm curious for thoughts on this, could PR it if it sounds acceptable)
Messages
right now locks its inner Mutex on every pass - could it possibly have aninto_inner()
method that locks once and returns the entire set? Adding on to this, could it have a method for checking if it's empty or not?The general idea here is that the common rendering case, as far as I understand, is something akin to:
The text was updated successfully, but these errors were encountered: