1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
use crate::middleware::OUTGOING_MAILBOX;
use std::fmt::{Debug, Display, Formatter};
#[derive(serde::Deserialize, serde::Serialize, Clone)]
/// A **one-time** user notification.
///
/// Flash messages are made of a [`Level`] and a string of content.
/// The message level can be used for filtering and rendering - for example:
///
/// - Only show flash messages at `info` level or above in a production environment, while retaining `debug` level messages for local development;
/// - Use different colours, in the UI, to display messages (e.g. red for errors, orange for warnings, etc.);
///
/// You can build a flash message via [`FlashMessage::new`] by specifying its content and [`Level`].
/// You can also use the shorter level-based constructors - e.g. [`FlashMessage::info`].
pub struct FlashMessage {
content: String,
level: Level,
}
impl FlashMessage {
/// Build a [`FlashMessage`] by specifying its content and [`Level`].
pub fn new(content: String, level: Level) -> Self {
Self { content, level }
}
/// The string content of this flash message.
pub fn content(&self) -> &str {
&self.content
}
/// The [`Level`] of this flash message.
pub fn level(&self) -> Level {
self.level
}
/// Build an info-level [`FlashMessage`] by specifying its content.
pub fn info<S: Into<String>>(content: S) -> Self {
Self {
content: content.into(),
level: Level::Info,
}
}
/// Build a debug-level [`FlashMessage`] by specifying its content.
pub fn debug<S: Into<String>>(content: S) -> Self {
Self {
content: content.into(),
level: Level::Debug,
}
}
/// Build a success-level [`FlashMessage`] by specifying its content.
pub fn success<S: Into<String>>(content: S) -> Self {
Self {
content: content.into(),
level: Level::Success,
}
}
/// Build a warning-level [`FlashMessage`] by specifying its content.
pub fn warning<S: Into<String>>(content: S) -> Self {
Self {
content: content.into(),
level: Level::Warning,
}
}
/// Build an error-level [`FlashMessage`] by specifying its content.
pub fn error<S: Into<String>>(content: S) -> Self {
Self {
content: content.into(),
level: Level::Error,
}
}
/// Attach this [`FlashMessage`] to the outgoing request.
///
/// The message will be dropped if its [`Level`] is below the minimum level
/// specified when configuring [`FlashMessagesFramework`] via [`FlashMessagesFrameworkBuilder::minimum_level`].
///
/// This method will **panic** if [`FlashMessagesFramework`] has not been registered as a middleware.
///
/// [`FlashMessagesFramework`]: crate::FlashMessagesFramework
/// [`FlashMessagesFrameworkBuilder::minimum_level`]: crate::FlashMessagesFrameworkBuilder::minimum_level
pub fn send(self) {
let result = OUTGOING_MAILBOX.try_with(|mailbox| {
if self.level as u8 >= mailbox.minimum_level as u8 {
mailbox.messages.borrow_mut().push(self);
}
});
if result.is_err() {
panic!("Failed to send flash message!\n\
To use `FlashMessages::send` you need to add `FlashMessageFramework` as a middleware \
on your `actix-web` application using `wrap`. Check out `actix-web-flash-messages`'s documentation for more details.")
}
}
}
#[derive(serde::Deserialize, serde::Serialize, Clone, Copy)]
/// The severity level of a [`FlashMessage`].
///
/// Levels can be used for filtering and rendering - for example:
///
/// - Only show flash messages at `info` level or above in a production environment, while retaining `debug` level messages for local development;
/// - Use different colours, in the UI, to display messages (e.g. red for errors, orange for warnings, etc.).
pub enum Level {
/// Development-related messages. Often ignored in a production environment.
Debug = 0,
/// Informational messages for the user - e.g. "Your last login was two days ago".
Info = 1,
/// Positive feedback after an action was successful - e.g. "You logged in successfully!".
Success = 2,
/// Notifying the user about an action that they must take imminently to prevent an error in the future.
Warning = 3,
/// An action was **not** successful - e.g. "The provided login credentials are invalid".
Error = 4,
}
impl Debug for Level {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", level_to_str(self))
}
}
impl Display for Level {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", level_to_str(self))
}
}
fn level_to_str(l: &Level) -> &'static str {
match l {
Level::Debug => "debug",
Level::Info => "info",
Level::Success => "success",
Level::Warning => "warning",
Level::Error => "error",
}
}