use async_trait::async_trait;
use std::collections::HashMap;
use std::sync::Arc;
use super::traits::{CrudRepository, PersistentEntity, RepositoryError};
use crate::http::CrudService;
type CreateMapperFn<C, E> = Box<dyn Fn(C) -> E + Send + Sync>;
type UpdateMapperFn<E, U> = Box<dyn Fn(&mut E, U) + Send + Sync>;
pub struct CrudServiceAdapter<R, E, C, U>
where
R: CrudRepository<E> + Send + Sync,
E: PersistentEntity,
C: Send + Sync,
U: Send + Sync,
{
repository: Arc<R>,
create_mapper: CreateMapperFn<C, E>,
update_mapper: UpdateMapperFn<E, U>,
#[allow(dead_code)]
entity_name: &'static str,
}
impl<R, E, C, U> CrudServiceAdapter<R, E, C, U>
where
R: CrudRepository<E> + Send + Sync,
E: PersistentEntity,
C: Send + Sync,
U: Send + Sync,
{
pub fn new<F1, F2>(
repository: Arc<R>,
entity_name: &'static str,
create_mapper: F1,
update_mapper: F2,
) -> Self
where
F1: Fn(C) -> E + Send + Sync + 'static,
F2: Fn(&mut E, U) + Send + Sync + 'static,
{
Self {
repository,
create_mapper: Box::new(create_mapper),
update_mapper: Box::new(update_mapper),
entity_name,
}
}
pub fn repository(&self) -> &R {
&self.repository
}
}
#[derive(Debug, thiserror::Error)]
pub enum AdapterError {
#[error("Repository error: {0}")]
Repository(#[from] RepositoryError),
#[error("Validation error: {0}")]
Validation(String),
#[error("Not found: {0}")]
NotFound(String),
}
impl From<AdapterError> for String {
fn from(e: AdapterError) -> Self {
e.to_string()
}
}
#[async_trait]
impl<R, E, C, U> CrudService<E, C, U> for CrudServiceAdapter<R, E, C, U>
where
R: CrudRepository<E> + Send + Sync + 'static,
E: PersistentEntity + Clone + 'static,
C: Send + Sync + 'static,
U: Send + Sync + 'static,
{
type Error = AdapterError;
fn entity_name() -> &'static str {
"Entity"
}
async fn list(
&self,
page: u32,
limit: u32,
_filters: HashMap<String, String>,
) -> Result<(Vec<E>, u64), Self::Error> {
Ok(self.repository.list(page, limit).await?)
}
async fn aggregate(
&self,
spec: &backbone_orm::repository::AggregateSpec,
filters: HashMap<String, String>,
) -> Result<backbone_orm::repository::AggregateResult, Self::Error> {
Ok(self.repository.aggregate_filtered(spec, filters).await?)
}
fn table_name(&self) -> Option<&str> {
self.repository.table_name()
}
async fn create(&self, dto: C) -> Result<E, Self::Error> {
let entity = (self.create_mapper)(dto);
Ok(self.repository.create(entity).await?)
}
async fn get_by_id(&self, id: &str) -> Result<Option<E>, Self::Error> {
Ok(self.repository.find_by_id(id).await?)
}
async fn update(&self, id: &str, dto: U) -> Result<Option<E>, Self::Error> {
let entity = self.repository.find_by_id(id).await?;
let Some(mut entity) = entity else {
return Ok(None);
};
(self.update_mapper)(&mut entity, dto);
Ok(Some(self.repository.update(entity).await?))
}
async fn partial_update(
&self,
id: &str,
_fields: HashMap<String, serde_json::Value>,
) -> Result<Option<E>, Self::Error> {
Ok(self.repository.find_by_id(id).await?)
}
async fn soft_delete(&self, id: &str) -> Result<bool, Self::Error> {
Ok(self.repository.soft_delete(id).await?)
}
async fn bulk_create(&self, items: Vec<C>) -> Result<Vec<E>, Self::Error> {
let entities: Vec<E> = items.into_iter().map(&*self.create_mapper).collect();
Ok(self.repository.bulk_create(entities).await?)
}
async fn upsert(&self, dto: C) -> Result<E, Self::Error> {
let entity = (self.create_mapper)(dto);
let id = entity.entity_id();
if let Some(existing) = self.repository.find_by_id(&id).await? {
Ok(self.repository.update(existing).await?)
} else {
Ok(self.repository.create(entity).await?)
}
}
async fn list_deleted(&self, page: u32, limit: u32) -> Result<(Vec<E>, u64), Self::Error> {
Ok(self.repository.list_deleted(page, limit).await?)
}
async fn restore(&self, id: &str) -> Result<Option<E>, Self::Error> {
Ok(self.repository.restore(id).await?)
}
async fn empty_trash(&self) -> Result<u64, Self::Error> {
Ok(self.repository.empty_trash().await?)
}
async fn get_deleted_by_id(&self, id: &str) -> Result<Option<E>, Self::Error> {
Ok(self.repository.find_by_id_including_deleted(id).await?)
}
async fn permanent_delete(&self, id: &str) -> Result<bool, Self::Error> {
Ok(self.repository.hard_delete(id).await?)
}
async fn list_deleted_filtered(&self, page: u32, limit: u32, filters: std::collections::HashMap<String, String>) -> Result<(Vec<E>, u64), Self::Error> {
let _ = filters;
Ok(self.repository.list_deleted(page, limit).await?)
}
async fn count_active(&self) -> Result<u64, Self::Error> {
Ok(self.repository.count().await?)
}
async fn count_deleted(&self) -> Result<u64, Self::Error> {
Ok(self.repository.count_deleted().await?)
}
}
pub struct SimpleCrudServiceAdapter<R, E>
where
R: CrudRepository<E> + Send + Sync,
E: PersistentEntity + Clone,
{
repository: Arc<R>,
#[allow(dead_code)]
entity_name: &'static str,
_phantom: std::marker::PhantomData<E>,
}
impl<R, E> SimpleCrudServiceAdapter<R, E>
where
R: CrudRepository<E> + Send + Sync,
E: PersistentEntity + Clone,
{
pub fn new(repository: Arc<R>, entity_name: &'static str) -> Self {
Self {
repository,
entity_name,
_phantom: std::marker::PhantomData,
}
}
}
#[async_trait]
impl<R, E> CrudService<E, E, E> for SimpleCrudServiceAdapter<R, E>
where
R: CrudRepository<E> + Send + Sync + 'static,
E: PersistentEntity + Clone + 'static,
{
type Error = AdapterError;
fn entity_name() -> &'static str {
"Entity"
}
async fn list(
&self,
page: u32,
limit: u32,
_filters: HashMap<String, String>,
) -> Result<(Vec<E>, u64), Self::Error> {
Ok(self.repository.list(page, limit).await?)
}
async fn aggregate(
&self,
spec: &backbone_orm::repository::AggregateSpec,
filters: HashMap<String, String>,
) -> Result<backbone_orm::repository::AggregateResult, Self::Error> {
Ok(self.repository.aggregate_filtered(spec, filters).await?)
}
fn table_name(&self) -> Option<&str> {
self.repository.table_name()
}
async fn create(&self, entity: E) -> Result<E, Self::Error> {
Ok(self.repository.create(entity).await?)
}
async fn get_by_id(&self, id: &str) -> Result<Option<E>, Self::Error> {
Ok(self.repository.find_by_id(id).await?)
}
async fn update(&self, id: &str, entity: E) -> Result<Option<E>, Self::Error> {
if self.repository.find_by_id(id).await?.is_none() {
return Ok(None);
}
Ok(Some(self.repository.update(entity).await?))
}
async fn partial_update(
&self,
id: &str,
_fields: HashMap<String, serde_json::Value>,
) -> Result<Option<E>, Self::Error> {
Ok(self.repository.find_by_id(id).await?)
}
async fn soft_delete(&self, id: &str) -> Result<bool, Self::Error> {
Ok(self.repository.soft_delete(id).await?)
}
async fn bulk_create(&self, items: Vec<E>) -> Result<Vec<E>, Self::Error> {
Ok(self.repository.bulk_create(items).await?)
}
async fn upsert(&self, entity: E) -> Result<E, Self::Error> {
let id = entity.entity_id();
if self.repository.find_by_id(&id).await?.is_some() {
Ok(self.repository.update(entity).await?)
} else {
Ok(self.repository.create(entity).await?)
}
}
async fn list_deleted(&self, page: u32, limit: u32) -> Result<(Vec<E>, u64), Self::Error> {
Ok(self.repository.list_deleted(page, limit).await?)
}
async fn restore(&self, id: &str) -> Result<Option<E>, Self::Error> {
Ok(self.repository.restore(id).await?)
}
async fn empty_trash(&self) -> Result<u64, Self::Error> {
Ok(self.repository.empty_trash().await?)
}
async fn get_deleted_by_id(&self, id: &str) -> Result<Option<E>, Self::Error> {
Ok(self.repository.find_by_id_including_deleted(id).await?)
}
async fn permanent_delete(&self, id: &str) -> Result<bool, Self::Error> {
Ok(self.repository.hard_delete(id).await?)
}
async fn list_deleted_filtered(&self, page: u32, limit: u32, filters: std::collections::HashMap<String, String>) -> Result<(Vec<E>, u64), Self::Error> {
let _ = filters;
Ok(self.repository.list_deleted(page, limit).await?)
}
async fn count_active(&self) -> Result<u64, Self::Error> {
Ok(self.repository.count().await?)
}
async fn count_deleted(&self) -> Result<u64, Self::Error> {
Ok(self.repository.count_deleted().await?)
}
}
use super::traits::SearchableRepository;
pub struct SearchableCrudServiceAdapter<R, E, C, U>
where
R: SearchableRepository<E> + Send + Sync,
E: PersistentEntity,
C: Send + Sync,
U: Send + Sync,
{
repository: Arc<R>,
create_mapper: CreateMapperFn<C, E>,
update_mapper: UpdateMapperFn<E, U>,
#[allow(dead_code)]
entity_name: &'static str,
}
impl<R, E, C, U> SearchableCrudServiceAdapter<R, E, C, U>
where
R: SearchableRepository<E> + Send + Sync,
E: PersistentEntity,
C: Send + Sync,
U: Send + Sync,
{
pub fn new<F1, F2>(
repository: Arc<R>,
entity_name: &'static str,
create_mapper: F1,
update_mapper: F2,
) -> Self
where
F1: Fn(C) -> E + Send + Sync + 'static,
F2: Fn(&mut E, U) + Send + Sync + 'static,
{
Self {
repository,
create_mapper: Box::new(create_mapper),
update_mapper: Box::new(update_mapper),
entity_name,
}
}
pub fn repository(&self) -> &R {
&self.repository
}
}
#[async_trait]
impl<R, E, C, U> CrudService<E, C, U> for SearchableCrudServiceAdapter<R, E, C, U>
where
R: SearchableRepository<E> + Send + Sync + 'static,
E: PersistentEntity + Clone + 'static,
C: Send + Sync + 'static,
U: Send + Sync + 'static,
{
type Error = AdapterError;
fn entity_name() -> &'static str {
"Entity"
}
async fn list(
&self,
page: u32,
limit: u32,
filters: HashMap<String, String>,
) -> Result<(Vec<E>, u64), Self::Error> {
if filters.is_empty() {
Ok(self.repository.list(page, limit).await?)
} else {
Ok(self.repository.search(filters, page, limit).await?)
}
}
async fn aggregate(
&self,
spec: &backbone_orm::repository::AggregateSpec,
filters: HashMap<String, String>,
) -> Result<backbone_orm::repository::AggregateResult, Self::Error> {
Ok(self.repository.aggregate_filtered(spec, filters).await?)
}
fn table_name(&self) -> Option<&str> {
self.repository.table_name()
}
async fn create(&self, dto: C) -> Result<E, Self::Error> {
let entity = (self.create_mapper)(dto);
Ok(self.repository.create(entity).await?)
}
async fn get_by_id(&self, id: &str) -> Result<Option<E>, Self::Error> {
Ok(self.repository.find_by_id(id).await?)
}
async fn update(&self, id: &str, dto: U) -> Result<Option<E>, Self::Error> {
let entity = self.repository.find_by_id(id).await?;
let Some(mut entity) = entity else {
return Ok(None);
};
(self.update_mapper)(&mut entity, dto);
Ok(Some(self.repository.update(entity).await?))
}
async fn partial_update(
&self,
id: &str,
_fields: HashMap<String, serde_json::Value>,
) -> Result<Option<E>, Self::Error> {
Ok(self.repository.find_by_id(id).await?)
}
async fn soft_delete(&self, id: &str) -> Result<bool, Self::Error> {
Ok(self.repository.soft_delete(id).await?)
}
async fn bulk_create(&self, items: Vec<C>) -> Result<Vec<E>, Self::Error> {
let entities: Vec<E> = items.into_iter().map(&*self.create_mapper).collect();
Ok(self.repository.bulk_create(entities).await?)
}
async fn upsert(&self, dto: C) -> Result<E, Self::Error> {
let entity = (self.create_mapper)(dto);
let id = entity.entity_id();
if let Some(existing) = self.repository.find_by_id(&id).await? {
Ok(self.repository.update(existing).await?)
} else {
Ok(self.repository.create(entity).await?)
}
}
async fn list_deleted(&self, page: u32, limit: u32) -> Result<(Vec<E>, u64), Self::Error> {
Ok(self.repository.list_deleted(page, limit).await?)
}
async fn restore(&self, id: &str) -> Result<Option<E>, Self::Error> {
Ok(self.repository.restore(id).await?)
}
async fn empty_trash(&self) -> Result<u64, Self::Error> {
Ok(self.repository.empty_trash().await?)
}
async fn get_deleted_by_id(&self, id: &str) -> Result<Option<E>, Self::Error> {
Ok(self.repository.find_by_id_including_deleted(id).await?)
}
async fn permanent_delete(&self, id: &str) -> Result<bool, Self::Error> {
Ok(self.repository.hard_delete(id).await?)
}
async fn list_deleted_filtered(&self, page: u32, limit: u32, filters: std::collections::HashMap<String, String>) -> Result<(Vec<E>, u64), Self::Error> {
let _ = filters;
Ok(self.repository.list_deleted(page, limit).await?)
}
async fn count_active(&self) -> Result<u64, Self::Error> {
Ok(self.repository.count().await?)
}
async fn count_deleted(&self) -> Result<u64, Self::Error> {
Ok(self.repository.count_deleted().await?)
}
}