sqlx_utils/traits/repository/transactions/save_tx.rs
1//! Extension to [`SaveRepository`] to add transaction based saving.
2
3use crate::prelude::{Model, SaveRepository, TransactionRepository};
4use std::future::Future;
5
6/// Extension trait for Save operations with transactions.
7///
8/// This trait provides convenience methods for using transactions with repositories
9/// that implement [`SaveRepository`]. It's automatically implemented for any type that
10/// implements both [`SaveRepository<M>`] and [`TransactionRepository<M>`].
11pub trait SaveRepositoryTransaction<M>: SaveRepository<M> + TransactionRepository<M>
12where
13 M: Model + Send + Sync,
14{
15 /// Saves a model in a transactions, ensuring atomicity.
16 ///
17 /// This method:
18 /// 1. Creates a transactions using [`with_transaction`](TransactionRepository)
19 /// 2. Calls [`save_with_executor`](SaveRepository::save_with_executor) with the transactions
20 /// 3. Returns the model on successful save
21 ///
22 /// # Parameters
23 ///
24 /// * `model`: The model to save
25 ///
26 /// # Returns
27 ///
28 /// A future that resolves to:
29 /// * `Ok(M)`: The saved model on success
30 /// * `Err(crate::Error)`: The error if saving failed
31 ///
32 /// # Example
33 ///
34 /// ```no_compile
35 /// let saved_model = repo.save_in_transaction(model).await?;
36 /// ```
37 fn save_in_transaction<'a>(
38 &'a self,
39 model: M,
40 ) -> impl Future<Output = Result<M, crate::Error>> + Send + 'a
41 where
42 M: 'a,
43 {
44 self.with_transaction(move |mut tx| async move {
45 let res = self.save_with_executor(&mut *tx, model).await;
46
47 (res, tx)
48 })
49 }
50}
51
52// Blanket implementation for any repository that implements both required traits
53impl<T, M> SaveRepositoryTransaction<M> for T
54where
55 T: SaveRepository<M> + TransactionRepository<M>,
56 M: Model + Send + Sync,
57{
58}