1use std::future::Future;
2use std::pin::Pin;
3
4use dinoco_engine::{DinocoAdapter, DinocoClient, DinocoResult, DinocoTransactionAdapter};
5
6use super::count::Count;
7use super::delete::DeleteWithCond;
8use super::delete_many::DeleteMany;
9use super::find_and_update::FindAndUpdate;
10use super::find_first::FindFirst;
11use super::find_many::FindMany;
12use super::insert_into::{
13 Insert, InsertReturning, InsertWithConnection, InsertWithConnectionReturning, InsertWithRelation,
14 InsertWithRelationReturning,
15};
16use super::insert_many::{
17 InsertMany, InsertManyReturning, InsertManyWithConnection, InsertManyWithConnectionReturning,
18 InsertManyWithConnections, InsertManyWithConnectionsReturning, InsertManyWithRelation,
19 InsertManyWithRelationReturning, InsertManyWithRelations, InsertManyWithRelationsReturning,
20};
21use super::update::{Update, UpdateReturning};
22use super::update_many::{UpdateMany, UpdateManyReturning};
23use crate::{InsertPayload, Projection, UpdateModel, UpdatePayload};
24
25type TransactionFuture<'a> = Pin<Box<dyn Future<Output = DinocoResult<()>> + Send + 'a>>;
26
27trait TransactionRunnable<A: DinocoAdapter>: Send {
28 fn run<'a>(self: Box<Self>, client: &'a DinocoClient<A>) -> TransactionFuture<'a>;
29}
30
31struct TransactionClosure<F> {
32 closure: Option<F>,
33}
34
35impl<A, F> TransactionRunnable<A> for TransactionClosure<F>
36where
37 A: DinocoAdapter,
38 F: for<'a> FnOnce(&'a DinocoClient<A>) -> TransactionFuture<'a> + Send + 'static,
39{
40 fn run<'a>(mut self: Box<Self>, client: &'a DinocoClient<A>) -> TransactionFuture<'a> {
41 let closure = self.closure.take().expect("transaction action already consumed");
42
43 closure(client)
44 }
45}
46
47pub struct TransactionAction<A: DinocoAdapter> {
48 runnable: Box<dyn TransactionRunnable<A>>,
49}
50
51impl<A> TransactionAction<A>
52where
53 A: DinocoAdapter,
54{
55 fn new<F>(closure: F) -> Self
56 where
57 F: for<'a> FnOnce(&'a DinocoClient<A>) -> TransactionFuture<'a> + Send + 'static,
58 {
59 Self { runnable: Box::new(TransactionClosure { closure: Some(closure) }) }
60 }
61
62 fn run<'a>(self, client: &'a DinocoClient<A>) -> TransactionFuture<'a> {
63 self.runnable.run(client)
64 }
65}
66
67pub trait IntoTransactionAction<A: DinocoAdapter> {
68 fn into_transaction_action(self) -> TransactionAction<A>;
69}
70
71pub trait TransactionActionExt<A: DinocoAdapter>: Sized {
72 fn tx(self) -> TransactionAction<A>
73 where
74 Self: IntoTransactionAction<A>,
75 {
76 self.into_transaction_action()
77 }
78}
79
80impl<A, T> TransactionActionExt<A> for T where A: DinocoAdapter {}
81
82pub fn tx<A, T>(action: T) -> TransactionAction<A>
83where
84 A: DinocoAdapter,
85 T: IntoTransactionAction<A>,
86{
87 action.into_transaction_action()
88}
89
90pub struct Transactions<A: DinocoAdapter> {
91 actions: Vec<TransactionAction<A>>,
92}
93
94pub fn transactions<A>(actions: Vec<TransactionAction<A>>) -> Transactions<A>
95where
96 A: DinocoAdapter,
97{
98 Transactions { actions }
99}
100
101impl<A> Transactions<A>
102where
103 A: DinocoAdapter + DinocoTransactionAdapter + Send + Sync,
104{
105 pub fn execute<'a>(self, client: &'a DinocoClient<A>) -> impl Future<Output = DinocoResult<()>> + Send + 'a
106 where
107 A: 'a,
108 {
109 async move {
110 let mut actions = self.actions;
111
112 client
113 .primary()
114 .with_transaction(move || {
115 Box::pin(async move {
116 for action in actions.drain(..) {
117 action.run(client).await?;
118 }
119
120 Ok(())
121 })
122 })
123 .await
124 }
125 }
126}
127
128impl<A, M> IntoTransactionAction<A> for Count<M>
129where
130 A: DinocoAdapter + Send + Sync + 'static,
131 M: crate::Model + Send + Sync + 'static,
132{
133 fn into_transaction_action(self) -> TransactionAction<A> {
134 TransactionAction::new(move |client| {
135 Box::pin(async move {
136 self.read_in_primary().execute(client).await?;
137
138 Ok(())
139 })
140 })
141 }
142}
143
144impl<A, M> IntoTransactionAction<A> for DeleteWithCond<M>
145where
146 A: DinocoAdapter + Send + Sync + 'static,
147 M: crate::Model + Send + Sync + 'static,
148{
149 fn into_transaction_action(self) -> TransactionAction<A> {
150 TransactionAction::new(move |client| Box::pin(async move { self.execute(client).await }))
151 }
152}
153
154impl<A, M> IntoTransactionAction<A> for DeleteMany<M>
155where
156 A: DinocoAdapter + Send + Sync + 'static,
157 M: crate::Model + Send + Sync + 'static,
158{
159 fn into_transaction_action(self) -> TransactionAction<A> {
160 TransactionAction::new(move |client| Box::pin(async move { self.execute(client).await }))
161 }
162}
163
164impl<A, M> IntoTransactionAction<A> for FindAndUpdate<M>
165where
166 A: DinocoAdapter + Send + Sync + 'static,
167 M: crate::FindAndUpdateModel + Send + Sync + 'static,
168{
169 fn into_transaction_action(self) -> TransactionAction<A> {
170 TransactionAction::new(move |client| {
171 Box::pin(async move {
172 self.execute(client).await?;
173
174 Ok(())
175 })
176 })
177 }
178}
179
180impl<A, M, S> IntoTransactionAction<A> for FindFirst<M, S>
181where
182 A: DinocoAdapter + Send + Sync + 'static,
183 M: crate::Model + Send + Sync + 'static,
184 S: Projection<M> + Send + Sync + 'static,
185{
186 fn into_transaction_action(self) -> TransactionAction<A> {
187 TransactionAction::new(move |client| {
188 Box::pin(async move {
189 self.read_in_primary().execute(client).await?;
190
191 Ok(())
192 })
193 })
194 }
195}
196
197impl<A, M, S> IntoTransactionAction<A> for FindMany<M, S>
198where
199 A: DinocoAdapter + Send + Sync + 'static,
200 M: crate::Model + Send + Sync + 'static,
201 S: Projection<M> + Send + Sync + 'static,
202{
203 fn into_transaction_action(self) -> TransactionAction<A> {
204 TransactionAction::new(move |client| {
205 Box::pin(async move {
206 self.read_in_primary().execute(client).await?;
207
208 Ok(())
209 })
210 })
211 }
212}
213
214impl<A, M, V> IntoTransactionAction<A> for Insert<M, V>
215where
216 A: DinocoAdapter + Send + Sync + 'static,
217 M: crate::InsertModel + Projection<M> + Send + Sync + 'static,
218 V: InsertPayload<M> + Send + 'static,
219 <V as InsertPayload<M>>::Nested: Send + 'static,
220{
221 fn into_transaction_action(self) -> TransactionAction<A> {
222 TransactionAction::new(move |client| Box::pin(async move { self.execute(client).await }))
223 }
224}
225
226impl<A, M, V, S> IntoTransactionAction<A> for InsertReturning<M, V, S>
227where
228 A: DinocoAdapter + Send + Sync + 'static,
229 M: crate::InsertModel + Projection<M> + Send + Sync + 'static,
230 V: InsertPayload<M> + Send + 'static,
231 <V as InsertPayload<M>>::Nested: Send + 'static,
232 S: Projection<M> + Send + Sync + 'static,
233{
234 fn into_transaction_action(self) -> TransactionAction<A> {
235 TransactionAction::new(move |client| {
236 Box::pin(async move {
237 self.execute(client).await?;
238
239 Ok(())
240 })
241 })
242 }
243}
244
245impl<A, M, R> IntoTransactionAction<A> for InsertWithRelation<M, R>
246where
247 A: DinocoAdapter + Send + Sync + 'static,
248 M: crate::InsertModel + crate::InsertRelation<R> + Projection<M> + Clone + Send + Sync + 'static,
249 R: crate::InsertModel + Projection<R> + Send + Sync + 'static,
250{
251 fn into_transaction_action(self) -> TransactionAction<A> {
252 TransactionAction::new(move |client| Box::pin(async move { self.execute(client).await }))
253 }
254}
255
256impl<A, M, R, S> IntoTransactionAction<A> for InsertWithRelationReturning<M, R, S>
257where
258 A: DinocoAdapter + Send + Sync + 'static,
259 M: crate::InsertModel + crate::InsertRelation<R> + Projection<M> + Clone + Send + Sync + 'static,
260 R: crate::InsertModel + Projection<R> + Send + Sync + 'static,
261 S: Projection<M> + Send + Sync + 'static,
262{
263 fn into_transaction_action(self) -> TransactionAction<A> {
264 TransactionAction::new(move |client| {
265 Box::pin(async move {
266 self.execute(client).await?;
267
268 Ok(())
269 })
270 })
271 }
272}
273
274impl<A, M, R> IntoTransactionAction<A> for InsertWithConnection<M, R>
275where
276 A: DinocoAdapter + Send + Sync + 'static,
277 M: crate::InsertModel + crate::InsertConnection<R> + Projection<M> + Send + Sync + 'static,
278 R: Send + Sync + 'static,
279{
280 fn into_transaction_action(self) -> TransactionAction<A> {
281 TransactionAction::new(move |client| Box::pin(async move { self.execute(client).await }))
282 }
283}
284
285impl<A, M, R, S> IntoTransactionAction<A> for InsertWithConnectionReturning<M, R, S>
286where
287 A: DinocoAdapter + Send + Sync + 'static,
288 M: crate::InsertModel + crate::InsertConnection<R> + Projection<M> + Clone + Send + Sync + 'static,
289 R: Send + Sync + 'static,
290 S: Projection<M> + Send + Sync + 'static,
291{
292 fn into_transaction_action(self) -> TransactionAction<A> {
293 TransactionAction::new(move |client| {
294 Box::pin(async move {
295 self.execute(client).await?;
296
297 Ok(())
298 })
299 })
300 }
301}
302
303impl<A, M, V> IntoTransactionAction<A> for InsertMany<M, V>
304where
305 A: DinocoAdapter + Send + Sync + 'static,
306 M: crate::InsertModel + Projection<M> + Send + Sync + 'static,
307 V: InsertPayload<M> + Send + 'static,
308 <V as InsertPayload<M>>::Nested: Send + 'static,
309{
310 fn into_transaction_action(self) -> TransactionAction<A> {
311 TransactionAction::new(move |client| Box::pin(async move { self.execute(client).await }))
312 }
313}
314
315impl<A, M, V, S> IntoTransactionAction<A> for InsertManyReturning<M, V, S>
316where
317 A: DinocoAdapter + Send + Sync + 'static,
318 M: crate::InsertModel + Projection<M> + Send + Sync + 'static,
319 V: InsertPayload<M> + Send + 'static,
320 <V as InsertPayload<M>>::Nested: Send + 'static,
321 S: Projection<M> + Send + Sync + 'static,
322{
323 fn into_transaction_action(self) -> TransactionAction<A> {
324 TransactionAction::new(move |client| {
325 Box::pin(async move {
326 self.execute(client).await?;
327
328 Ok(())
329 })
330 })
331 }
332}
333
334impl<A, M, R> IntoTransactionAction<A> for InsertManyWithRelation<M, R>
335where
336 A: DinocoAdapter + Send + Sync + 'static,
337 M: crate::InsertModel + crate::InsertRelation<R> + Projection<M> + Clone + Send + Sync + 'static,
338 R: crate::InsertModel + Projection<R> + Send + Sync + 'static,
339{
340 fn into_transaction_action(self) -> TransactionAction<A> {
341 TransactionAction::new(move |client| Box::pin(async move { self.execute(client).await }))
342 }
343}
344
345impl<A, M, R, S> IntoTransactionAction<A> for InsertManyWithRelationReturning<M, R, S>
346where
347 A: DinocoAdapter + Send + Sync + 'static,
348 M: crate::InsertModel + crate::InsertRelation<R> + Projection<M> + Clone + Send + Sync + 'static,
349 R: crate::InsertModel + Projection<R> + Send + Sync + 'static,
350 S: Projection<M> + Send + Sync + 'static,
351{
352 fn into_transaction_action(self) -> TransactionAction<A> {
353 TransactionAction::new(move |client| {
354 Box::pin(async move {
355 self.execute(client).await?;
356
357 Ok(())
358 })
359 })
360 }
361}
362
363impl<A, M, R> IntoTransactionAction<A> for InsertManyWithRelations<M, R>
364where
365 A: DinocoAdapter + Send + Sync + 'static,
366 M: crate::InsertModel + crate::InsertRelation<R> + Projection<M> + Clone + Send + Sync + 'static,
367 R: crate::InsertModel + Projection<R> + Clone + Send + Sync + 'static,
368{
369 fn into_transaction_action(self) -> TransactionAction<A> {
370 TransactionAction::new(move |client| Box::pin(async move { self.execute(client).await }))
371 }
372}
373
374impl<A, M, R, S> IntoTransactionAction<A> for InsertManyWithRelationsReturning<M, R, S>
375where
376 A: DinocoAdapter + Send + Sync + 'static,
377 M: crate::InsertModel + crate::InsertRelation<R> + Projection<M> + Clone + Send + Sync + 'static,
378 R: crate::InsertModel + Projection<R> + Clone + Send + Sync + 'static,
379 S: Projection<M> + Send + Sync + 'static,
380{
381 fn into_transaction_action(self) -> TransactionAction<A> {
382 TransactionAction::new(move |client| {
383 Box::pin(async move {
384 self.execute(client).await?;
385
386 Ok(())
387 })
388 })
389 }
390}
391
392impl<A, M, R> IntoTransactionAction<A> for InsertManyWithConnections<M, R>
393where
394 A: DinocoAdapter + Send + Sync + 'static,
395 M: crate::InsertModel + crate::InsertConnection<R> + Projection<M> + Send + Sync + 'static,
396 R: Send + Sync + 'static,
397{
398 fn into_transaction_action(self) -> TransactionAction<A> {
399 TransactionAction::new(move |client| Box::pin(async move { self.execute(client).await }))
400 }
401}
402
403impl<A, M, R, S> IntoTransactionAction<A> for InsertManyWithConnectionsReturning<M, R, S>
404where
405 A: DinocoAdapter + Send + Sync + 'static,
406 M: crate::InsertModel + crate::InsertConnection<R> + Projection<M> + Clone + Send + Sync + 'static,
407 R: Send + Sync + 'static,
408 S: Projection<M> + Send + Sync + 'static,
409{
410 fn into_transaction_action(self) -> TransactionAction<A> {
411 TransactionAction::new(move |client| {
412 Box::pin(async move {
413 self.execute(client).await?;
414
415 Ok(())
416 })
417 })
418 }
419}
420
421impl<A, M, R> IntoTransactionAction<A> for InsertManyWithConnection<M, R>
422where
423 A: DinocoAdapter + Send + Sync + 'static,
424 M: crate::InsertModel + crate::InsertConnection<R> + Projection<M> + Send + Sync + 'static,
425 R: Send + Sync + 'static,
426{
427 fn into_transaction_action(self) -> TransactionAction<A> {
428 TransactionAction::new(move |client| Box::pin(async move { self.execute(client).await }))
429 }
430}
431
432impl<A, M, R, S> IntoTransactionAction<A> for InsertManyWithConnectionReturning<M, R, S>
433where
434 A: DinocoAdapter + Send + Sync + 'static,
435 M: crate::InsertModel + crate::InsertConnection<R> + Projection<M> + Clone + Send + Sync + 'static,
436 R: Send + Sync + 'static,
437 S: Projection<M> + Send + Sync + 'static,
438{
439 fn into_transaction_action(self) -> TransactionAction<A> {
440 TransactionAction::new(move |client| {
441 Box::pin(async move {
442 self.execute(client).await?;
443
444 Ok(())
445 })
446 })
447 }
448}
449
450impl<A, M, V> IntoTransactionAction<A> for Update<M, V>
451where
452 A: DinocoAdapter + Send + Sync + 'static,
453 M: UpdateModel + Send + Sync + 'static,
454 V: UpdatePayload<M> + Send + Sync + 'static,
455{
456 fn into_transaction_action(self) -> TransactionAction<A> {
457 TransactionAction::new(move |client| Box::pin(async move { self.execute(client).await }))
458 }
459}
460
461impl<A, M, V, S> IntoTransactionAction<A> for UpdateReturning<M, V, S>
462where
463 A: DinocoAdapter + Send + Sync + 'static,
464 M: UpdateModel + Projection<M> + Send + Sync + 'static,
465 V: UpdatePayload<M> + Send + Sync + 'static,
466 S: Projection<M> + Send + Sync + 'static,
467{
468 fn into_transaction_action(self) -> TransactionAction<A> {
469 TransactionAction::new(move |client| {
470 Box::pin(async move {
471 self.execute(client).await?;
472
473 Ok(())
474 })
475 })
476 }
477}
478
479impl<A, M, V> IntoTransactionAction<A> for UpdateMany<M, V>
480where
481 A: DinocoAdapter + Send + Sync + 'static,
482 M: UpdateModel + Send + Sync + 'static,
483 V: UpdatePayload<M> + Send + Sync + 'static,
484{
485 fn into_transaction_action(self) -> TransactionAction<A> {
486 TransactionAction::new(move |client| Box::pin(async move { self.execute(client).await }))
487 }
488}
489
490impl<A, M, V, S> IntoTransactionAction<A> for UpdateManyReturning<M, V, S>
491where
492 A: DinocoAdapter + Send + Sync + 'static,
493 M: UpdateModel + Send + Sync + 'static,
494 V: UpdatePayload<M> + Send + Sync + 'static,
495 S: Projection<M> + Send + Sync + 'static,
496{
497 fn into_transaction_action(self) -> TransactionAction<A> {
498 TransactionAction::new(move |client| {
499 Box::pin(async move {
500 self.execute(client).await?;
501
502 Ok(())
503 })
504 })
505 }
506}