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}