trillium/init.rs
1use crate::{Handler, Info};
2use std::{future::Future, mem};
3
4/// Provides support for asynchronous initialization of a handler after
5/// the server is started.
6///
7/// ```
8/// use trillium::{Conn, Init, State};
9/// use trillium_testing::TestServer;
10///
11/// #[derive(Debug, Clone, PartialEq)]
12/// struct MyDatabaseConnection(String);
13/// impl MyDatabaseConnection {
14/// async fn connect(uri: &str) -> std::io::Result<Self> {
15/// Ok(Self(uri.into()))
16/// }
17///
18/// async fn query(&self, query: &str) -> String {
19/// format!("you queried `{}` against {}", query, &self.0)
20/// }
21/// }
22///
23/// # trillium_testing::block_on(async {
24/// let handler = (
25/// Init::new(|mut info| async move {
26/// let db = MyDatabaseConnection::connect("db://db").await.expect("1");
27/// info.with_shared_state(db)
28/// }),
29/// |conn: Conn| async move {
30/// let db = conn.shared_state::<MyDatabaseConnection>().expect("2");
31/// let response = db.query("select * from users limit 1").await;
32/// conn.ok(response)
33/// },
34/// );
35///
36/// let app = TestServer::new(handler).await;
37/// app.assert_shared_state(MyDatabaseConnection("db://db".into()));
38/// app.get("/")
39/// .await
40/// .assert_ok()
41/// .assert_body("you queried `select * from users limit 1` against db://db");
42/// # });
43/// ```
44#[derive(Debug)]
45pub struct Init<F>(Option<F>);
46
47impl<F, Fut> Init<F>
48where
49 F: FnOnce(Info) -> Fut + Send + Sync + 'static,
50 Fut: Future<Output = Info> + Send + 'static,
51{
52 /// Constructs a new Init handler with an async function that receives and returns [`Info`].
53 #[must_use]
54 pub const fn new(init: F) -> Self {
55 Self(Some(init))
56 }
57}
58
59impl<F, Fut> Handler for Init<F>
60where
61 F: FnOnce(Info) -> Fut + Send + Sync + 'static,
62 Fut: Future<Output = Info> + Send + 'static,
63{
64 async fn init(&mut self, info: &mut Info) {
65 match self.0.take() {
66 Some(init) => {
67 *info = init(mem::take(info)).await;
68 }
69 _ => {
70 log::warn!("called init more than once");
71 }
72 }
73 }
74}
75
76/// alias for [`Init::new`]
77pub const fn init<F, Fut>(init: F) -> Init<F>
78where
79 F: FnOnce(Info) -> Fut + Send + Sync + 'static,
80 Fut: Future<Output = Info> + Send + 'static,
81{
82 Init::new(init)
83}