1use async_trait::async_trait;
2use std::fmt::Debug;
3use std::marker::PhantomData;
4
5use crate::testdb::{DatabaseBackend, DatabaseConfig};
6
7pub mod boxed;
8mod setup;
9mod with_database;
10mod with_transaction;
11
12#[cfg(test)]
13mod tests;
14
15pub use boxed::{BoxedDatabaseEntryPoint, with_boxed_database, with_boxed_database_config};
17pub use setup::{SetupHandler, setup};
18pub use with_database::DatabaseHandler;
19pub use with_transaction::{
20 DatabaseTransactionHandler, TransactionFnHandler, with_db_transaction, with_transaction,
21};
22
23pub struct AndThenHandler<DB, A, B, F>
26where
27 DB: DatabaseBackend + Send + Sync + Debug + 'static,
28 A: TransactionHandler<DB> + Send + Sync,
29 B: TransactionHandler<DB, Error = A::Error> + Send + Sync,
30 F: FnOnce(A::Item) -> B + Send + Sync + 'static,
31{
32 first: A,
33 next_fn: F,
34 _phantom: PhantomData<(DB, B)>,
35}
36
37#[async_trait]
38impl<DB, A, B, F> TransactionHandler<DB> for AndThenHandler<DB, A, B, F>
39where
40 DB: DatabaseBackend + Send + Sync + Debug + 'static,
41 A: TransactionHandler<DB> + Send + Sync,
42 B: TransactionHandler<DB, Error = A::Error> + Send + Sync,
43 F: FnOnce(A::Item) -> B + Send + Sync + 'static,
44{
45 type Item = B::Item;
46 type Error = A::Error;
47
48 async fn execute(self, ctx: &mut crate::TestContext<DB>) -> Result<Self::Item, Self::Error> {
49 let result = self.first.execute(ctx).await?;
50 let next = (self.next_fn)(result);
51 next.execute(ctx).await
52 }
53}
54
55pub use boxed::{
89 with_boxed_database as with_database, with_boxed_database_config as with_database_config,
90};
91
92#[async_trait]
94pub trait TransactionHandler<DB>: Send + Sync
95where
96 DB: DatabaseBackend + Send + Sync + Debug + 'static,
97{
98 type Item;
100 type Error: From<DB::Error> + Send + Sync;
102
103 async fn execute(self, ctx: &mut crate::TestContext<DB>) -> Result<Self::Item, Self::Error>;
105
106 async fn execute_standalone(self, backend: DB) -> Result<Self::Item, Self::Error>
111 where
112 Self: Sized,
113 {
114 let config = DatabaseConfig::default();
116 let db_instance = crate::testdb::TestDatabaseInstance::new(backend, config).await?;
117 let mut ctx = crate::TestContext::new(db_instance);
118
119 self.execute(&mut ctx).await
121 }
122
123 fn and_then<F, B>(self, f: F) -> AndThenHandler<DB, Self, B, F>
125 where
126 Self: Sized,
127 B: TransactionHandler<DB, Error = Self::Error> + Send + Sync,
128 F: FnOnce(Self::Item) -> B + Send + Sync + 'static,
129 {
130 AndThenHandler {
131 first: self,
132 next_fn: f,
133 _phantom: PhantomData,
134 }
135 }
136
137 fn setup<S, Fut, E>(
139 self,
140 setup_fn: S,
141 ) -> impl TransactionHandler<DB, Item = (), Error = Self::Error>
142 where
143 Self: Sized,
144 E: From<DB::Error> + From<Self::Error> + Send + Sync,
145 Fut: std::future::Future<Output = Result<(), DB::Error>> + Send + 'static,
146 S: FnOnce(&mut <DB::Pool as crate::DatabasePool>::Connection) -> Fut
147 + Send
148 + Sync
149 + 'static,
150 {
151 self.and_then(move |_| {
152 let handler = setup(setup_fn);
153 SetupHandlerWrapper::<DB, S, Self::Error>::new(handler)
154 })
155 }
156
157 fn with_transaction<F, Fut, E>(
159 self,
160 transaction_fn: F,
161 ) -> impl TransactionHandler<DB, Item = (), Error = Self::Error>
162 where
163 Self: Sized,
164 E: From<DB::Error> + From<Self::Error> + Send + Sync,
165 Fut: std::future::Future<Output = Result<(), DB::Error>> + Send + 'static,
166 F: FnOnce(&mut <DB as DatabaseBackend>::Connection) -> Fut + Send + Sync + 'static,
167 {
168 self.and_then(move |_| {
169 let handler = with_transaction(transaction_fn);
170 TransactionFnHandlerWrapper::<DB, F, Self::Error>::new(handler)
171 })
172 }
173
174 fn with_db_transaction<F, Fut, E>(
176 self,
177 db: crate::TestDatabaseInstance<DB>,
178 transaction_fn: F,
179 ) -> impl TransactionHandler<DB, Item = crate::TestContext<DB>, Error = Self::Error>
180 where
181 Self: Sized,
182 E: From<DB::Error> + From<Self::Error> + Send + Sync,
183 Fut: std::future::Future<Output = Result<(), DB::Error>> + Send + 'static,
184 F: FnOnce(&mut <DB as DatabaseBackend>::Connection) -> Fut + Send + Sync + 'static,
185 {
186 self.and_then(move |_| {
187 let handler = with_db_transaction(db, transaction_fn);
188 DbTransactionHandlerWrapper::<DB, F, Self::Error>::new(handler)
189 })
190 }
191
192 async fn run_with_database(self, backend: DB) -> Result<crate::TestContext<DB>, Self::Error>
194 where
195 Self: Sized,
196 {
197 let config = DatabaseConfig::default();
198 let db_instance = crate::testdb::TestDatabaseInstance::new(backend, config).await?;
199 let mut ctx = crate::TestContext::new(db_instance);
200
201 self.execute(&mut ctx).await?;
202
203 Ok(ctx)
204 }
205}
206
207pub trait IntoTransactionHandler<DB>
209where
210 DB: DatabaseBackend + Send + Sync + Debug + 'static,
211{
212 type Handler: TransactionHandler<DB, Item = Self::Item, Error = Self::Error>;
214 type Item;
216 type Error: From<DB::Error> + Send + Sync;
218
219 fn into_transaction_handler(self) -> Self::Handler;
221}
222
223pub async fn run_with_database<DB, H>(
225 backend: DB,
226 handler: H,
227) -> Result<crate::TestContext<DB>, H::Error>
228where
229 DB: DatabaseBackend + Send + Sync + Debug + 'static,
230 H: TransactionHandler<DB>,
231{
232 handler.run_with_database(backend).await
233}
234
235pub struct SetupHandlerWrapper<DB, S, E>
237where
238 DB: DatabaseBackend + Send + Sync + Debug + 'static,
239 S: Send + Sync + 'static,
240 E: From<DB::Error> + Send + Sync,
241{
242 inner: SetupHandler<DB, S>,
243 _error: PhantomData<E>,
244}
245
246impl<DB, S, E> SetupHandlerWrapper<DB, S, E>
247where
248 DB: DatabaseBackend + Send + Sync + Debug + 'static,
249 S: Send + Sync + 'static,
250 E: From<DB::Error> + Send + Sync,
251{
252 pub fn new(inner: SetupHandler<DB, S>) -> Self {
253 Self {
254 inner,
255 _error: PhantomData,
256 }
257 }
258}
259
260#[async_trait]
261impl<DB, S, Fut, E> TransactionHandler<DB> for SetupHandlerWrapper<DB, S, E>
262where
263 DB: DatabaseBackend + Send + Sync + Debug + 'static,
264 Fut: std::future::Future<Output = Result<(), DB::Error>> + Send + 'static,
265 S: FnOnce(&mut <DB::Pool as crate::DatabasePool>::Connection) -> Fut + Send + Sync + 'static,
266 E: From<DB::Error> + Send + Sync,
267{
268 type Item = ();
269 type Error = E;
270
271 async fn execute(self, ctx: &mut crate::TestContext<DB>) -> Result<Self::Item, Self::Error> {
272 Ok(self.inner.execute(ctx).await?)
273 }
274}
275
276pub struct TransactionFnHandlerWrapper<DB, F, E>
278where
279 DB: DatabaseBackend + Send + Sync + Debug + 'static,
280 F: Send + Sync + 'static,
281 E: From<DB::Error> + Send + Sync,
282{
283 inner: TransactionFnHandler<DB, F>,
284 _error: PhantomData<E>,
285}
286
287impl<DB, F, E> TransactionFnHandlerWrapper<DB, F, E>
288where
289 DB: DatabaseBackend + Send + Sync + Debug + 'static,
290 F: Send + Sync + 'static,
291 E: From<DB::Error> + Send + Sync,
292{
293 pub fn new(inner: TransactionFnHandler<DB, F>) -> Self {
294 Self {
295 inner,
296 _error: PhantomData,
297 }
298 }
299}
300
301#[async_trait]
302impl<DB, F, Fut, E> TransactionHandler<DB> for TransactionFnHandlerWrapper<DB, F, E>
303where
304 DB: DatabaseBackend + Send + Sync + Debug + 'static,
305 Fut: std::future::Future<Output = Result<(), DB::Error>> + Send + 'static,
306 F: FnOnce(&mut <DB as DatabaseBackend>::Connection) -> Fut + Send + Sync + 'static,
307 E: From<DB::Error> + Send + Sync,
308{
309 type Item = ();
310 type Error = E;
311
312 async fn execute(self, ctx: &mut crate::TestContext<DB>) -> Result<Self::Item, Self::Error> {
313 Ok(self.inner.execute(ctx).await?)
314 }
315}
316
317pub struct DbTransactionHandlerWrapper<DB, F, E>
319where
320 DB: DatabaseBackend + Send + Sync + Debug + 'static,
321 F: Send + Sync + 'static,
322 E: From<DB::Error> + Send + Sync,
323{
324 inner: DatabaseTransactionHandler<DB, F>,
325 _error: PhantomData<E>,
326}
327
328impl<DB, F, E> DbTransactionHandlerWrapper<DB, F, E>
329where
330 DB: DatabaseBackend + Send + Sync + Debug + 'static,
331 F: Send + Sync + 'static,
332 E: From<DB::Error> + Send + Sync,
333{
334 pub fn new(inner: DatabaseTransactionHandler<DB, F>) -> Self {
335 Self {
336 inner,
337 _error: PhantomData,
338 }
339 }
340}
341
342#[async_trait]
343impl<DB, F, Fut, E> TransactionHandler<DB> for DbTransactionHandlerWrapper<DB, F, E>
344where
345 DB: DatabaseBackend + Send + Sync + Debug + 'static,
346 Fut: std::future::Future<Output = Result<(), DB::Error>> + Send + 'static,
347 F: FnOnce(&mut <DB as DatabaseBackend>::Connection) -> Fut + Send + Sync + 'static,
348 E: From<DB::Error> + Send + Sync,
349{
350 type Item = crate::TestContext<DB>;
351 type Error = E;
352
353 async fn execute(self, ctx: &mut crate::TestContext<DB>) -> Result<Self::Item, Self::Error> {
354 Ok(self.inner.execute(ctx).await?)
355 }
356}