#[cfg(feature = "async")]
use std::future::Future;
use std::ops::Deref;
use arangors::client::reqwest::ReqwestClient;
use arangors::transaction::Transaction as TransactionLayer;
pub use {
transaction_builder::TransactionBuilder, transaction_output::TransactionOutput,
transaction_pool::TransactionPool,
};
use crate::{DatabaseConnectionPool, ServiceError};
mod transaction_builder;
mod transaction_output;
mod transaction_pool;
pub struct Transaction {
accessor: TransactionLayer<ReqwestClient>,
pool: TransactionPool,
}
impl Transaction {
#[maybe_async::maybe_async]
pub async fn new(db_pool: &DatabaseConnectionPool) -> Result<Self, ServiceError> {
TransactionBuilder::new().build(db_pool).await
}
#[maybe_async::maybe_async]
pub async fn commit(&self) -> Result<(), ServiceError> {
let status = self.accessor.commit().await?;
log::debug!("Transaction committed with status: {:?}", status);
Ok(())
}
#[maybe_async::maybe_async]
pub async fn abort(&self) -> Result<(), ServiceError> {
let status = self.accessor.abort().await?;
log::debug!("Transaction aborted with status: {:?}", status);
Ok(())
}
#[cfg(feature = "async")]
pub async fn safe_execute<T, O, F>(
&self,
operations: O,
) -> Result<TransactionOutput<T>, ServiceError>
where
O: FnOnce(TransactionPool) -> F,
F: Future<Output = Result<T, ServiceError>>,
{
log::trace!("Safely executing transactional operations..");
let res = operations(self.pool.clone()).await;
log::trace!(
"Safely executing transactional operations.. Done. Success: {}",
res.is_ok()
);
self.handle_safe_execute(res).await
}
#[cfg(not(feature = "async"))]
pub fn safe_execute<T, O>(&self, operations: O) -> Result<TransactionOutput<T>, ServiceError>
where
O: FnOnce(TransactionPool) -> Result<T, ServiceError>,
{
let res = operations(self.pool.clone());
self.handle_safe_execute(res)
}
#[maybe_async::maybe_async]
async fn handle_safe_execute<T>(
&self,
result: Result<T, ServiceError>,
) -> Result<TransactionOutput<T>, ServiceError> {
match result {
Ok(value) => {
log::debug!("Transaction succeeded. Committing..");
self.commit().await?;
Ok(TransactionOutput::Committed(value))
}
Err(err) => {
log::debug!("Transaction failed with: {}. Aborting..", err);
self.abort().await?;
Ok(TransactionOutput::Aborted(err))
}
}
}
pub fn pool(&self) -> &TransactionPool {
&self.pool
}
}
impl Deref for Transaction {
type Target = TransactionPool;
fn deref(&self) -> &Self::Target {
&self.pool
}
}