use std::marker::PhantomData;
use std::sync::Arc;
use aro_core::error::RepoError;
use aro_core::repository::{BoxFuture, Repository};
use aro_web::dep::Dep;
use sqlx::Database;
use crate::error::from_fletch_err;
pub trait FletchDatabase: Database + Send + Sync + 'static {}
#[cfg(feature = "sqlite")]
impl FletchDatabase for sqlx::Sqlite {}
#[cfg(feature = "postgres")]
impl FletchDatabase for sqlx::Postgres {}
#[cfg(feature = "mysql")]
impl FletchDatabase for sqlx::MySql {}
pub trait FletchEntity<DB: FletchDatabase>:
aro_core::entity::Entity
+ fletch_orm::Entity<Id = <Self as aro_core::entity::Entity>::Id>
+ for<'r> sqlx::FromRow<'r, DB::Row>
+ Send
+ Sync
+ Unpin
+ Clone
{
}
impl<T, DB> FletchEntity<DB> for T
where
DB: FletchDatabase,
T: aro_core::entity::Entity
+ fletch_orm::Entity<Id = <T as aro_core::entity::Entity>::Id>
+ for<'r> sqlx::FromRow<'r, DB::Row>
+ Send
+ Sync
+ Unpin
+ Clone,
{
}
pub struct FletchRepository<T, DB: FletchDatabase> {
pool: fletch_orm::Pool<DB>,
_marker: PhantomData<T>,
}
impl<T, DB> FletchRepository<T, DB>
where
DB: FletchDatabase,
{
pub fn new(pool: fletch_orm::Pool<DB>) -> Self {
Self {
pool,
_marker: PhantomData,
}
}
pub fn from_dep(dep: &Dep<fletch_orm::Pool<DB>>) -> Self {
Self::new((**dep).clone())
}
pub fn pool(&self) -> &fletch_orm::Pool<DB> {
&self.pool
}
}
pub struct FletchTransactionalRepository<T, DB: FletchDatabase> {
state: Arc<crate::transaction::FletchTransactionState<DB>>,
_marker: PhantomData<T>,
}
impl<T, DB> FletchTransactionalRepository<T, DB>
where
DB: FletchDatabase,
{
pub(crate) fn new(state: Arc<crate::transaction::FletchTransactionState<DB>>) -> Self {
Self {
state,
_marker: PhantomData,
}
}
}
macro_rules! impl_transactional_repository {
(mysql, $db:ty, $dialect:expr) => {
impl<T> FletchTransactionalRepository<T, $db>
where
T: FletchEntity<$db>,
{
pub async fn find_by_id(
&self,
id: <T as aro_core::entity::Entity>::Id,
) -> Result<T, RepoError> {
let mut guard = self.state.tx.lock().await;
let tx = guard
.as_mut()
.ok_or_else(|| RepoError::Unknown("transaction is no longer active".into()))?;
fletch_orm::find_by_id($dialect, &mut **tx.inner_mut(), id)
.await
.map_err(from_fletch_err)
}
pub async fn find_all(&self) -> Result<Vec<T>, RepoError> {
let mut guard = self.state.tx.lock().await;
let tx = guard
.as_mut()
.ok_or_else(|| RepoError::Unknown("transaction is no longer active".into()))?;
fletch_orm::find_all($dialect, &mut **tx.inner_mut())
.await
.map_err(from_fletch_err)
}
pub async fn create(&self, entity: T) -> Result<T, RepoError> {
let mut guard = self.state.tx.lock().await;
let tx = guard
.as_mut()
.ok_or_else(|| RepoError::Unknown("transaction is no longer active".into()))?;
tx.insert(&entity).await.map_err(from_fletch_err)
}
pub async fn update(&self, entity: T) -> Result<T, RepoError> {
let mut guard = self.state.tx.lock().await;
let tx = guard
.as_mut()
.ok_or_else(|| RepoError::Unknown("transaction is no longer active".into()))?;
tx.update(&entity).await.map_err(from_fletch_err)
}
pub async fn delete(
&self,
id: <T as aro_core::entity::Entity>::Id,
) -> Result<(), RepoError> {
let mut guard = self.state.tx.lock().await;
let tx = guard
.as_mut()
.ok_or_else(|| RepoError::Unknown("transaction is no longer active".into()))?;
fletch_orm::delete::<T, $db, _, _>($dialect, &mut **tx.inner_mut(), id)
.await
.map_err(from_fletch_err)
}
}
};
(returning, $db:ty, $dialect:expr) => {
impl<T> FletchTransactionalRepository<T, $db>
where
T: FletchEntity<$db>,
{
pub async fn find_by_id(
&self,
id: <T as aro_core::entity::Entity>::Id,
) -> Result<T, RepoError> {
let mut guard = self.state.tx.lock().await;
let tx = guard
.as_mut()
.ok_or_else(|| RepoError::Unknown("transaction is no longer active".into()))?;
fletch_orm::find_by_id($dialect, &mut **tx.inner_mut(), id)
.await
.map_err(from_fletch_err)
}
pub async fn find_all(&self) -> Result<Vec<T>, RepoError> {
let mut guard = self.state.tx.lock().await;
let tx = guard
.as_mut()
.ok_or_else(|| RepoError::Unknown("transaction is no longer active".into()))?;
fletch_orm::find_all($dialect, &mut **tx.inner_mut())
.await
.map_err(from_fletch_err)
}
pub async fn create(&self, entity: T) -> Result<T, RepoError> {
let mut guard = self.state.tx.lock().await;
let tx = guard
.as_mut()
.ok_or_else(|| RepoError::Unknown("transaction is no longer active".into()))?;
fletch_orm::insert($dialect, &mut **tx.inner_mut(), &entity)
.await
.map_err(from_fletch_err)
}
pub async fn update(&self, entity: T) -> Result<T, RepoError> {
let mut guard = self.state.tx.lock().await;
let tx = guard
.as_mut()
.ok_or_else(|| RepoError::Unknown("transaction is no longer active".into()))?;
fletch_orm::update($dialect, &mut **tx.inner_mut(), &entity)
.await
.map_err(from_fletch_err)
}
pub async fn delete(
&self,
id: <T as aro_core::entity::Entity>::Id,
) -> Result<(), RepoError> {
let mut guard = self.state.tx.lock().await;
let tx = guard
.as_mut()
.ok_or_else(|| RepoError::Unknown("transaction is no longer active".into()))?;
fletch_orm::delete::<T, $db, _, _>($dialect, &mut **tx.inner_mut(), id)
.await
.map_err(from_fletch_err)
}
}
};
}
#[cfg(feature = "sqlite")]
impl_transactional_repository!(returning, sqlx::Sqlite, &fletch_orm::SqliteDialect);
#[cfg(feature = "postgres")]
impl_transactional_repository!(returning, sqlx::Postgres, &fletch_orm::PostgresDialect);
#[cfg(feature = "mysql")]
impl_transactional_repository!(mysql, sqlx::MySql, &fletch_orm::MySqlDialect);
macro_rules! impl_repository {
($db:ty) => {
impl<T> Repository<T> for FletchRepository<T, $db>
where
T: FletchEntity<$db>,
<T as aro_core::entity::Entity>::Id: Into<fletch_orm::Value>,
{
fn find_by_id(
&self,
id: <T as aro_core::entity::Entity>::Id,
) -> BoxFuture<'_, Result<T, RepoError>> {
Box::pin(
async move { self.pool.find_by_id::<T>(id).await.map_err(from_fletch_err) },
)
}
fn find_all(&self) -> BoxFuture<'_, Result<Vec<T>, RepoError>> {
Box::pin(async move { self.pool.find_all::<T>().await.map_err(from_fletch_err) })
}
fn create(&self, entity: T) -> BoxFuture<'_, Result<T, RepoError>> {
Box::pin(async move {
self.pool
.insert::<T>(&entity)
.await
.map_err(from_fletch_err)
})
}
fn update(&self, entity: T) -> BoxFuture<'_, Result<T, RepoError>> {
Box::pin(async move {
self.pool
.update::<T>(&entity)
.await
.map_err(from_fletch_err)
})
}
fn delete(
&self,
id: <T as aro_core::entity::Entity>::Id,
) -> BoxFuture<'_, Result<(), RepoError>> {
Box::pin(async move { self.pool.delete::<T>(id).await.map_err(from_fletch_err) })
}
}
};
}
#[cfg(feature = "sqlite")]
impl_repository!(sqlx::Sqlite);
#[cfg(feature = "postgres")]
impl_repository!(sqlx::Postgres);
#[cfg(feature = "mysql")]
impl_repository!(sqlx::MySql);