Skip to main content

doido_controller/
flash.rs

1//! Flash messages: values set on one request and read on exactly the next one.
2//!
3//! The flash is carried between requests in a signed cookie (reusing
4//! [`CookieSessionStore`]). Because the next request does not re-write it unless
5//! new messages are set, a flash naturally lives for a single following request
6//! (Rails' `flash[:notice]`).
7
8use crate::session::{CookieSessionStore, Session};
9use std::collections::BTreeMap;
10
11/// A bag of one-shot messages keyed by name (e.g. `notice`, `alert`).
12#[derive(Debug, Default, Clone)]
13pub struct Flash {
14    messages: BTreeMap<String, String>,
15}
16
17impl Flash {
18    pub fn new() -> Self {
19        Self::default()
20    }
21
22    /// Set a flash message under `key` (e.g. `flash.set("notice", "Saved!")`).
23    pub fn set(&mut self, key: impl Into<String>, message: impl Into<String>) {
24        self.messages.insert(key.into(), message.into());
25    }
26
27    /// Read a flash message by key.
28    pub fn get(&self, key: &str) -> Option<&str> {
29        self.messages.get(key).map(String::as_str)
30    }
31
32    /// Whether there are no messages.
33    pub fn is_empty(&self) -> bool {
34        self.messages.is_empty()
35    }
36
37    /// Iterate `(key, message)` pairs, e.g. to render them all in a layout.
38    pub fn iter(&self) -> impl Iterator<Item = (&String, &String)> {
39        self.messages.iter()
40    }
41
42    /// Serialize and sign the flash into a cookie value for the next request.
43    pub fn to_cookie(&self, store: &CookieSessionStore) -> String {
44        let mut session = Session::with_id("flash");
45        session.set("flash", &self.messages);
46        store.encode(&session)
47    }
48
49    /// Read the flash from a signed cookie value, yielding an empty flash when
50    /// the cookie is absent, malformed, or fails signature verification.
51    pub fn from_cookie(store: &CookieSessionStore, raw: &str) -> Self {
52        let messages = store
53            .decode(raw)
54            .and_then(|s| s.get::<BTreeMap<String, String>>("flash"))
55            .unwrap_or_default();
56        Self { messages }
57    }
58}