modo/flash/mod.rs
1//! # modo::flash
2//!
3//! Cookie-based flash messages for one-time cross-request notifications.
4//!
5//! This module is always available; no feature flag is required.
6//!
7//! Provides:
8//!
9//! - [`FlashLayer`] — Tower [`Layer`](tower::Layer) that enables flash cookie support on a router.
10//! - [`FlashMiddleware`] — Tower [`Service`](tower::Service) produced by [`FlashLayer`].
11//! - [`Flash`] — axum extractor for writing and reading flash messages in handlers.
12//! - [`FlashEntry`] — a single message carrying a severity `level` and `message` text.
13//!
14//! Flash messages are stored in a signed cookie and cleared after being read.
15//! They survive exactly one redirect: the current request writes a message and
16//! the next request reads it. Once read, the cookie is removed from the response.
17//!
18//! Requires [`FlashLayer`] to be applied to the router before using the [`Flash`] extractor.
19//!
20//! When the `templates` feature is enabled, `TemplateContextLayer` automatically
21//! injects a `flash_messages()` callable into every MiniJinja template context.
22//! Calling it from a template is equivalent to calling [`Flash::messages`] from a
23//! handler — it marks the messages as consumed and clears the cookie on the response.
24//!
25//! ## Quick start
26//!
27//! ```rust,ignore
28//! use modo::cookie::{CookieConfig, key_from_config};
29//! use modo::flash::FlashLayer;
30//! use modo::Flash;
31//! use axum::{Router, routing::{get, post}, response::Redirect};
32//!
33//! // Build the layer from your cookie config
34//! let config: CookieConfig = app_config.cookie.clone();
35//! let key = key_from_config(&config).unwrap();
36//!
37//! let app = Router::new()
38//! .route("/form", post(submit_handler))
39//! .route("/result", get(result_handler))
40//! .layer(FlashLayer::new(&config, &key));
41//!
42//! // Write a flash message and redirect
43//! async fn submit_handler(flash: Flash) -> Redirect {
44//! flash.success("Record saved.");
45//! Redirect::to("/result")
46//! }
47//!
48//! // Read flash messages on the next request
49//! async fn result_handler(flash: Flash) -> String {
50//! let msgs = flash.messages();
51//! msgs.iter()
52//! .map(|m| format!("[{}] {}", m.level, m.message))
53//! .collect::<Vec<_>>()
54//! .join("\n")
55//! }
56//! ```
57
58mod extractor;
59mod middleware;
60pub(crate) mod state;
61
62pub use extractor::Flash;
63pub use middleware::{FlashLayer, FlashMiddleware};
64pub use state::FlashEntry;