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
use crate::storage::FlashMessageStore;
use crate::Level;
use std::sync::Arc;

#[derive(Clone)]
/// `actix-web` middleware providing support for sending and receiving [`FlashMessage`]s.
///
/// Use [`FlashMessagesFramework::builder`] to build an instance of [`FlashMessagesFramework`]!
///
/// ```rust
/// use actix_web_flash_messages::{FlashMessagesFramework, storage::CookieMessageStore};
/// use actix_web::{HttpServer, App, web, cookie::Key};
///
/// #[actix_web::main]
/// async fn main() {
///     let signing_key = Key::generate(); // This will usually come from configuration!
///     let message_store = CookieMessageStore::builder(signing_key).build();
///     let message_framework = FlashMessagesFramework::builder(message_store).build();
///
///     HttpServer::new(move || {
///         App::new()
///             .wrap(message_framework.clone())
///         // [...] your endpoints
///     })
///     # ;
/// }
/// ```
///
/// [`FlashMessage`]: crate::FlashMessage
pub struct FlashMessagesFramework {
    pub(crate) minimum_level: Level,
    pub(crate) storage_backend: Arc<dyn FlashMessageStore>,
}

impl FlashMessagesFramework {
    /// A fluent API to configure [`FlashMessagesFramework`].
    ///
    /// It takes as input a **message store**, the only required piece of configuration.  
    ///
    /// `actix-web-flash-messages` provides a cookie-based implementation of flash messages, [`CookieMessageStore`],
    /// using a signed cookie to store and retrieve messages.  
    /// You can provide your own custom message store backend by implementing the [`FlashMessageStore`] trait.
    ///
    /// [`CookieMessageStore`]: crate::storage::CookieMessageStore
    pub fn builder<S: FlashMessageStore + 'static>(
        storage_backend: S,
    ) -> FlashMessagesFrameworkBuilder {
        FlashMessagesFrameworkBuilder {
            minimum_level: None,
            storage_backend: Arc::new(storage_backend),
        }
    }
}

/// A fluent builder to construct a [`FlashMessagesFramework`] instance.
pub struct FlashMessagesFrameworkBuilder {
    pub(crate) minimum_level: Option<Level>,
    pub(crate) storage_backend: Arc<dyn FlashMessageStore>,
}

impl FlashMessagesFrameworkBuilder {
    /// By default, [`FlashMessagesFramework`] will only dispatch messages at `info`-level or above, discarding `debug`-level messages.
    /// You can change this behaviour using this method:
    ///
    /// ```rust
    /// use actix_web_flash_messages::{FlashMessagesFramework, Level, storage::CookieMessageStore};
    /// use actix_web::{HttpServer, App, web};
    ///
    /// fn get_message_store() -> CookieMessageStore {
    ///     // [...]
    ///     # CookieMessageStore::builder(actix_web::cookie::Key::generate()).build()
    /// }
    ///
    /// #[actix_web::main]
    /// async fn main() {
    ///     // Show debug-level messages when developing locally
    ///     let minimum_level = match std::env::var("APP_ENV") {
    ///         Ok(s) if &s == "local" => Level::Debug,
    ///         _ => Level::Info,
    ///     };
    ///     let message_framework = FlashMessagesFramework::builder(get_message_store())
    ///         .minimum_level(minimum_level)
    ///         .build();
    ///
    ///     HttpServer::new(move || {
    ///         App::new()
    ///             .wrap(message_framework.clone())
    ///         // [...] Your endpoints
    ///     })
    ///     # ;
    /// }
    /// ```
    pub fn minimum_level(mut self, minimum_level: Level) -> Self {
        self.minimum_level = Some(minimum_level);
        self
    }

    /// Finalise the builder and return a [`FlashMessagesFramework`] instance.
    pub fn build(self) -> FlashMessagesFramework {
        FlashMessagesFramework {
            minimum_level: self.minimum_level.unwrap_or(Level::Info),
            storage_backend: self.storage_backend,
        }
    }
}