Skip to main content

trillium/
state.rs

1use crate::{Conn, Handler};
2use std::fmt::Debug;
3
4/// # A handler for sharing state across an application.
5///
6/// State is a handler that puts a clone of any `Clone + Send + Sync +
7/// 'static` type into every conn's state map.
8///
9/// ```
10/// use std::sync::{
11///     Arc,
12///     atomic::{AtomicBool, Ordering},
13/// };
14/// use trillium::{Conn, State};
15/// use trillium_testing::TestServer;
16///
17/// #[derive(Clone, Default)] // Clone is mandatory
18/// struct MyFeatureFlag(Arc<AtomicBool>);
19///
20/// impl MyFeatureFlag {
21///     pub fn is_enabled(&self) -> bool {
22///         self.0.load(Ordering::Relaxed)
23///     }
24///
25///     pub fn toggle(&self) {
26///         self.0.fetch_xor(true, Ordering::Relaxed);
27///     }
28/// }
29///
30/// # trillium_testing::block_on(async {
31/// let feature_flag = MyFeatureFlag::default();
32///
33/// let handler = (State::new(feature_flag.clone()), |conn: Conn| async move {
34///     if conn.state::<MyFeatureFlag>().unwrap().is_enabled() {
35///         conn.ok("feature enabled")
36///     } else {
37///         conn.ok("not enabled")
38///     }
39/// });
40///
41/// let app = TestServer::new(handler).await;
42///
43/// assert!(!feature_flag.is_enabled());
44/// app.get("/").await.assert_ok().assert_body("not enabled");
45/// app.get("/").await.assert_ok().assert_body("not enabled");
46/// feature_flag.toggle();
47/// assert!(feature_flag.is_enabled());
48/// app.get("/")
49///     .await
50///     .assert_ok()
51///     .assert_body("feature enabled");
52/// app.get("/")
53///     .await
54///     .assert_ok()
55///     .assert_body("feature enabled");
56/// # });
57/// ```
58///
59/// Please note that as with the above contrived example, if your state
60/// needs to be mutable, you need to choose your own interior mutability
61/// with whatever cross thread synchronization mechanisms are appropriate
62/// for your application. There will be one clones of the contained T type
63/// in memory for each http connection, and any locks should be held as
64/// briefly as possible so as to minimize impact on other conns.
65#[derive(Debug, Default)]
66pub struct State<T: Clone + Send + Sync + 'static>(T);
67
68impl<T> State<T>
69where
70    T: Clone + Send + Sync + 'static,
71{
72    /// Constructs a new State handler from any `Clone` + `Send` + `Sync` +
73    /// `'static`
74    pub const fn new(t: T) -> Self {
75        Self(t)
76    }
77}
78
79/// Constructs a new [`State`] handler from any Clone + Send + Sync +
80/// 'static. Alias for [`State::new`]
81pub const fn state<T: Clone + Send + Sync + 'static>(t: T) -> State<T> {
82    State::new(t)
83}
84
85impl<T: Clone + Send + Sync + 'static> Handler for State<T> {
86    async fn run(&self, mut conn: Conn) -> Conn {
87        conn.insert_state(self.0.clone());
88        conn
89    }
90}