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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
use crate::{async_trait, Conn, Handler, Info, Upgrade};
use std::{
borrow::Cow,
fmt::{self, Debug, Formatter},
future::Future,
mem,
ops::Deref,
pin::Pin,
};
/**
Provides support for asynchronous initialization of a handler after
the server is started.
```
use trillium::{Conn, State, Init};
#[derive(Debug, Clone)]
struct MyDatabaseConnection(String);
impl MyDatabaseConnection {
async fn connect(uri: String) -> std::io::Result<Self> {
Ok(Self(uri))
}
async fn query(&mut self, query: &str) -> String {
format!("you queried `{}` against {}", query, &self.0)
}
}
let mut handler = (
Init::new(|_| async {
let url = std::env::var("DATABASE_URL").unwrap();
let db = MyDatabaseConnection::connect(url).await.unwrap();
State::new(db)
}),
|mut conn: Conn| async move {
let db = conn.state_mut::<MyDatabaseConnection>().unwrap();
let response = db.query("select * from users limit 1").await;
conn.ok(response)
}
);
std::env::set_var("DATABASE_URL", "db://db");
use trillium_testing::prelude::*;
init(&mut handler);
assert_ok!(
get("/").on(&handler),
"you queried `select * from users limit 1` against db://db"
);
```
Because () is the noop handler, this can also be used to perform one-time set up:
```
use trillium::{Init, Conn};
let mut handler = (
Init::new(|info| async move { log::info!("{}", info); }),
|conn: Conn| async move { conn.ok("ok!") }
);
use trillium_testing::prelude::*;
init(&mut handler);
assert_ok!(get("/").on(&handler), "ok!");
```
*/
pub struct Init<T>(Inner<T>);
type Initializer<T> = Box<
dyn FnOnce(Info) -> Pin<Box<dyn Future<Output = T> + Send + 'static>> + Send + Sync + 'static,
>;
enum Inner<T> {
New(Initializer<T>),
Initializing,
Initialized(T),
}
impl<T: Handler> Deref for Inner<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Self::Initialized(t) => t,
_ => {
panic!("attempted to dereference uninitialized handler {:?}", &self);
}
}
}
}
impl<T: Handler> Debug for Inner<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Initialized(ref t) => f.debug_tuple("Initialized").field(&t.name()).finish(),
Self::New(_) => f
.debug_tuple("New")
.field(&std::any::type_name::<T>())
.finish(),
Self::Initializing => f
.debug_tuple("Initializing")
.field(&std::any::type_name::<T>())
.finish(),
}
}
}
impl<T: Handler> Debug for Init<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("Init").field(&self.0).finish()
}
}
impl<T: Handler> Init<T> {
/**
Constructs a new Init handler with an async function that
returns the handler post-initialization. The async function
receives [`Info`] for the current server.
*/
pub fn new<F, Fut>(init: F) -> Self
where
F: FnOnce(Info) -> Fut + Send + Sync + 'static,
Fut: Future<Output = T> + Send + 'static,
{
Self(Inner::New(Box::new(move |info| Box::pin(init(info)))))
}
}
#[async_trait]
impl<T: Handler> Handler for Init<T> {
async fn run(&self, conn: Conn) -> Conn {
self.0.run(conn).await
}
async fn init(&mut self, info: &mut Info) {
self.0 = match mem::replace(&mut self.0, Inner::Initializing) {
Inner::New(init) => {
let mut initialized = init(info.clone()).await;
initialized.init(info).await;
Inner::Initialized(initialized)
}
other => other,
}
}
async fn before_send(&self, conn: Conn) -> Conn {
self.0.before_send(conn).await
}
fn has_upgrade(&self, upgrade: &Upgrade) -> bool {
self.0.has_upgrade(upgrade)
}
async fn upgrade(&self, upgrade: Upgrade) {
self.0.upgrade(upgrade).await;
}
fn name(&self) -> Cow<'static, str> {
match &self.0 {
Inner::New(_) => format!("uninitialized {}", std::any::type_name::<T>()).into(),
Inner::Initializing => {
format!("currently initializing {}", std::any::type_name::<T>()).into()
}
Inner::Initialized(t) => t.name(),
}
}
}
/// alias for [`Init::new`]
pub fn init<T, F, Fut>(init: F) -> Init<T>
where
F: FnOnce(Info) -> Fut + Send + Sync + 'static,
Fut: Future<Output = T> + Send + 'static,
T: Handler,
{
Init::new(init)
}