pub fn log_include_hydration_failure(table: &str, error: &str) {
tracing::error!(
relation_table = table,
error = error,
"include hydration failed; relation expanded as null"
);
}
#[macro_export]
macro_rules! impl_crud_repository {
($repo:ty, $entity:ty, soft_delete) => {
#[async_trait::async_trait]
impl backbone_core::CrudRepository<$entity> for $repo {
async fn fetch_related_json(
&self,
table: &str,
ids: &[String],
) -> Vec<serde_json::Value> {
match backbone_orm::fetch_by_ids_as_json(
(&**self).pool(),
(&**self).table_name(),
table,
ids,
)
.await
{
Ok(rows) => rows,
Err(err) => {
backbone_core::log_include_hydration_failure(table, &err.to_string());
Vec::new()
}
}
}
async fn create(
&self,
entity: $entity,
) -> Result<$entity, backbone_core::RepositoryError> {
(&**self).create(&entity)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn find_by_id(
&self,
id: &str,
) -> Result<Option<$entity>, backbone_core::RepositoryError> {
(&**self).find_by_id(id)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn find_by_id_including_deleted(
&self,
id: &str,
) -> Result<Option<$entity>, backbone_core::RepositoryError> {
(&**self).find_deleted_by_id(id)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn update(
&self,
entity: $entity,
) -> Result<$entity, backbone_core::RepositoryError> {
let id = backbone_core::PersistentEntity::entity_id(&entity);
(&**self).update(&id, &entity)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
.and_then(|opt| opt.ok_or(backbone_core::RepositoryError::NotFound))
}
async fn soft_delete(
&self,
id: &str,
) -> Result<bool, backbone_core::RepositoryError> {
(&**self).soft_delete(id)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn restore(
&self,
id: &str,
) -> Result<Option<$entity>, backbone_core::RepositoryError> {
(&**self).restore(id)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn hard_delete(
&self,
id: &str,
) -> Result<bool, backbone_core::RepositoryError> {
(&**self).permanent_delete(id)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn list(
&self,
page: u32,
limit: u32,
) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
let pagination =
backbone_orm::repository::PaginationParams { page, per_page: limit };
(&**self).list_paginated(pagination)
.await
.map(|r| (r.data, r.pagination.total))
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn list_filtered(
&self,
page: u32,
limit: u32,
filters: std::collections::HashMap<String, String>,
) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
let pagination =
backbone_orm::repository::PaginationParams { page, per_page: limit };
(&**self).list_paginated_filtered(pagination, Some(&filters))
.await
.map(|r| (r.data, r.pagination.total))
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn list_filtered_with_info(
&self,
page: u32,
limit: u32,
filters: std::collections::HashMap<String, String>,
) -> Result<
(Vec<$entity>, backbone_orm::repository::PaginationInfo),
backbone_core::RepositoryError,
> {
let pagination =
backbone_orm::repository::PaginationParams { page, per_page: limit };
(&**self)
.list_paginated_filtered(pagination, Some(&filters))
.await
.map(|r| (r.data, r.pagination))
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
fn table_name(&self) -> Option<&str> {
Some((&**self).table_name())
}
async fn aggregate_filtered(
&self,
spec: &backbone_orm::repository::AggregateSpec,
filters: std::collections::HashMap<String, String>,
) -> Result<backbone_orm::repository::AggregateResult, backbone_core::RepositoryError> {
(&**self).aggregate_filtered(spec, Some(&filters))
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn list_deleted(
&self,
page: u32,
limit: u32,
) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
let pagination =
backbone_orm::repository::PaginationParams { page, per_page: limit };
(&**self).list_deleted(pagination)
.await
.map(|r| (r.data, r.pagination.total))
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn count(&self) -> Result<u64, backbone_core::RepositoryError> {
(&**self).count_active()
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn count_deleted(&self) -> Result<u64, backbone_core::RepositoryError> {
(&**self).count_deleted()
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn bulk_create(
&self,
entities: Vec<$entity>,
) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
let mut results = Vec::with_capacity(entities.len());
for entity in entities {
let created = (&**self)
.create(&entity)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))?;
results.push(created);
}
Ok(results)
}
async fn empty_trash(&self) -> Result<u64, backbone_core::RepositoryError> {
(&**self).empty_trash()
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn bulk_soft_delete(
&self,
ids: &[String],
) -> Result<u64, backbone_core::RepositoryError> {
(&**self).bulk_soft_delete(ids)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn bulk_restore(
&self,
ids: &[String],
) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
(&**self).bulk_restore(ids)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn bulk_hard_delete(
&self,
ids: &[String],
) -> Result<u64, backbone_core::RepositoryError> {
(&**self).bulk_permanent_delete(ids)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn restore_all(&self) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
(&**self).restore_all()
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn bulk_update(
&self,
entities: Vec<$entity>,
) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
(&**self).bulk_update(&entities)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
}
};
($repo:ty, $entity:ty, no_soft_delete) => {
#[async_trait::async_trait]
impl backbone_core::CrudRepository<$entity> for $repo {
async fn fetch_related_json(
&self,
table: &str,
ids: &[String],
) -> Vec<serde_json::Value> {
match backbone_orm::fetch_by_ids_as_json(
(&**self).pool(),
(&**self).table_name(),
table,
ids,
)
.await
{
Ok(rows) => rows,
Err(err) => {
backbone_core::log_include_hydration_failure(table, &err.to_string());
Vec::new()
}
}
}
async fn create(
&self,
entity: $entity,
) -> Result<$entity, backbone_core::RepositoryError> {
(&**self).create(&entity)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn find_by_id(
&self,
id: &str,
) -> Result<Option<$entity>, backbone_core::RepositoryError> {
(&**self).find_by_id(id)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn find_by_id_including_deleted(
&self,
id: &str,
) -> Result<Option<$entity>, backbone_core::RepositoryError> {
(&**self).find_by_id(id)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn update(
&self,
entity: $entity,
) -> Result<$entity, backbone_core::RepositoryError> {
let id = backbone_core::PersistentEntity::entity_id(&entity);
(&**self).update(&id, &entity)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
.and_then(|opt| opt.ok_or(backbone_core::RepositoryError::NotFound))
}
async fn soft_delete(
&self,
id: &str,
) -> Result<bool, backbone_core::RepositoryError> {
(&**self).delete(id)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn restore(
&self,
id: &str,
) -> Result<Option<$entity>, backbone_core::RepositoryError> {
(&**self).find_by_id(id)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn hard_delete(
&self,
id: &str,
) -> Result<bool, backbone_core::RepositoryError> {
(&**self).delete(id)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn list(
&self,
page: u32,
limit: u32,
) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
let pagination =
backbone_orm::repository::PaginationParams { page, per_page: limit };
(&**self).list_paginated(pagination)
.await
.map(|r| (r.data, r.pagination.total))
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn list_filtered(
&self,
page: u32,
limit: u32,
filters: std::collections::HashMap<String, String>,
) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
let pagination =
backbone_orm::repository::PaginationParams { page, per_page: limit };
(&**self).list_paginated_filtered(pagination, Some(&filters))
.await
.map(|r| (r.data, r.pagination.total))
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn list_filtered_with_info(
&self,
page: u32,
limit: u32,
filters: std::collections::HashMap<String, String>,
) -> Result<
(Vec<$entity>, backbone_orm::repository::PaginationInfo),
backbone_core::RepositoryError,
> {
let pagination =
backbone_orm::repository::PaginationParams { page, per_page: limit };
(&**self)
.list_paginated_filtered(pagination, Some(&filters))
.await
.map(|r| (r.data, r.pagination))
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
fn table_name(&self) -> Option<&str> {
Some((&**self).table_name())
}
async fn aggregate_filtered(
&self,
spec: &backbone_orm::repository::AggregateSpec,
filters: std::collections::HashMap<String, String>,
) -> Result<backbone_orm::repository::AggregateResult, backbone_core::RepositoryError> {
(&**self).aggregate_filtered(spec, Some(&filters))
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn list_deleted(
&self,
_page: u32,
_limit: u32,
) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
Ok((vec![], 0))
}
async fn count(&self) -> Result<u64, backbone_core::RepositoryError> {
(&**self).count()
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn count_deleted(&self) -> Result<u64, backbone_core::RepositoryError> {
Ok(0)
}
async fn bulk_create(
&self,
entities: Vec<$entity>,
) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
let mut results = Vec::with_capacity(entities.len());
for entity in entities {
let created = (&**self)
.create(&entity)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))?;
results.push(created);
}
Ok(results)
}
async fn empty_trash(&self) -> Result<u64, backbone_core::RepositoryError> {
Ok(0)
}
async fn bulk_soft_delete(
&self,
ids: &[String],
) -> Result<u64, backbone_core::RepositoryError> {
(&**self).bulk_delete(ids)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn bulk_restore(
&self,
_ids: &[String],
) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
Ok(Vec::new())
}
async fn bulk_hard_delete(
&self,
ids: &[String],
) -> Result<u64, backbone_core::RepositoryError> {
(&**self).bulk_delete(ids)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
async fn restore_all(&self) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
Ok(Vec::new())
}
async fn bulk_update(
&self,
entities: Vec<$entity>,
) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
(&**self).bulk_update(&entities)
.await
.map_err(|e| backbone_core::RepositoryError::DatabaseError(e.to_string()))
}
}
};
}