1pub fn log_include_hydration_failure(table: &str, error: &str) {
24 tracing::error!(
25 relation_table = table,
26 error = error,
27 "include hydration failed; relation expanded as null"
28 );
29}
30
31#[macro_export]
69macro_rules! impl_crud_repository {
70 ($repo:ty, $entity:ty, soft_delete) => {
76 #[async_trait::async_trait]
77 impl backbone_core::CrudRepository<$entity> for $repo {
78 async fn fetch_related_json(
79 &self,
80 table: &str,
81 ids: &[String],
82 ) -> Vec<serde_json::Value> {
83 match backbone_orm::fetch_by_ids_as_json(
84 (&**self).pool(),
85 (&**self).table_name(),
86 table,
87 ids,
88 )
89 .await
90 {
91 Ok(rows) => rows,
92 Err(err) => {
93 backbone_core::log_include_hydration_failure(table, &err.to_string());
94 Vec::new()
95 }
96 }
97 }
98
99 async fn create(
107 &self,
108 entity: $entity,
109 ) -> Result<$entity, backbone_core::RepositoryError> {
110 (&**self).create(&entity)
111 .await
112 .map_err(backbone_core::RepositoryError::from_source)
113 }
114
115 async fn find_by_id(
116 &self,
117 id: &str,
118 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
119 (&**self).find_by_id(id)
120 .await
121 .map_err(backbone_core::RepositoryError::from_source)
122 }
123
124 async fn find_by_id_including_deleted(
125 &self,
126 id: &str,
127 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
128 (&**self).find_deleted_by_id(id)
129 .await
130 .map_err(backbone_core::RepositoryError::from_source)
131 }
132
133 async fn update(
134 &self,
135 entity: $entity,
136 ) -> Result<$entity, backbone_core::RepositoryError> {
137 let id = backbone_core::PersistentEntity::entity_id(&entity);
138 (&**self).update(&id, &entity)
139 .await
140 .map_err(backbone_core::RepositoryError::from_source)
141 .and_then(|opt| opt.ok_or(backbone_core::RepositoryError::NotFound))
142 }
143
144 async fn update_changed(
145 &self,
146 before: &$entity,
147 after: $entity,
148 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
149 let id = backbone_core::PersistentEntity::entity_id(&after);
150 (&**self)
151 .update_changed(&id, before, &after)
152 .await
153 .map_err(backbone_core::RepositoryError::from_source)
154 }
155
156 async fn soft_delete(
157 &self,
158 id: &str,
159 ) -> Result<bool, backbone_core::RepositoryError> {
160 (&**self).soft_delete(id)
161 .await
162 .map_err(backbone_core::RepositoryError::from_source)
163 }
164
165 async fn restore(
166 &self,
167 id: &str,
168 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
169 (&**self).restore(id)
170 .await
171 .map_err(backbone_core::RepositoryError::from_source)
172 }
173
174 async fn hard_delete(
175 &self,
176 id: &str,
177 ) -> Result<bool, backbone_core::RepositoryError> {
178 (&**self).permanent_delete(id)
179 .await
180 .map_err(backbone_core::RepositoryError::from_source)
181 }
182
183 async fn list(
184 &self,
185 page: u32,
186 limit: u32,
187 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
188 let pagination =
189 backbone_orm::repository::PaginationParams { page, per_page: limit };
190 (&**self).list_paginated(pagination)
191 .await
192 .map(|r| (r.data, r.pagination.total))
193 .map_err(backbone_core::RepositoryError::from_source)
194 }
195
196 async fn list_filtered(
197 &self,
198 page: u32,
199 limit: u32,
200 filters: std::collections::HashMap<String, String>,
201 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
202 let pagination =
203 backbone_orm::repository::PaginationParams { page, per_page: limit };
204 (&**self).list_paginated_filtered(pagination, Some(&filters))
205 .await
206 .map(|r| (r.data, r.pagination.total))
207 .map_err(backbone_core::RepositoryError::from_source)
208 }
209
210 async fn list_filtered_with_info(
211 &self,
212 page: u32,
213 limit: u32,
214 filters: std::collections::HashMap<String, String>,
215 ) -> Result<
216 (Vec<$entity>, backbone_orm::repository::PaginationInfo),
217 backbone_core::RepositoryError,
218 > {
219 let pagination =
220 backbone_orm::repository::PaginationParams { page, per_page: limit };
221 (&**self)
222 .list_paginated_filtered(pagination, Some(&filters))
223 .await
224 .map(|r| (r.data, r.pagination))
225 .map_err(backbone_core::RepositoryError::from_source)
226 }
227
228 fn table_name(&self) -> Option<&str> {
229 Some((&**self).table_name())
230 }
231
232 async fn aggregate_filtered(
233 &self,
234 spec: &backbone_orm::repository::AggregateSpec,
235 filters: std::collections::HashMap<String, String>,
236 ) -> Result<backbone_orm::repository::AggregateResult, backbone_core::RepositoryError> {
237 (&**self).aggregate_filtered(spec, Some(&filters))
238 .await
239 .map_err(backbone_core::RepositoryError::from_source)
240 }
241
242 async fn list_deleted(
243 &self,
244 page: u32,
245 limit: u32,
246 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
247 let pagination =
248 backbone_orm::repository::PaginationParams { page, per_page: limit };
249 (&**self).list_deleted(pagination)
250 .await
251 .map(|r| (r.data, r.pagination.total))
252 .map_err(backbone_core::RepositoryError::from_source)
253 }
254
255 async fn count(&self) -> Result<u64, backbone_core::RepositoryError> {
256 (&**self).count_active()
257 .await
258 .map_err(backbone_core::RepositoryError::from_source)
259 }
260
261 async fn count_deleted(&self) -> Result<u64, backbone_core::RepositoryError> {
262 (&**self).count_deleted()
263 .await
264 .map_err(backbone_core::RepositoryError::from_source)
265 }
266
267 async fn bulk_create(
268 &self,
269 entities: Vec<$entity>,
270 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
271 let mut results = Vec::with_capacity(entities.len());
272 for entity in entities {
273 let created = (&**self)
274 .create(&entity)
275 .await
276 .map_err(backbone_core::RepositoryError::from_source)?;
277 results.push(created);
278 }
279 Ok(results)
280 }
281
282 async fn empty_trash(&self) -> Result<u64, backbone_core::RepositoryError> {
283 (&**self).empty_trash()
284 .await
285 .map_err(backbone_core::RepositoryError::from_source)
286 }
287
288 async fn bulk_soft_delete(
291 &self,
292 ids: &[String],
293 ) -> Result<u64, backbone_core::RepositoryError> {
294 (&**self).bulk_soft_delete(ids)
295 .await
296 .map_err(backbone_core::RepositoryError::from_source)
297 }
298
299 async fn bulk_restore(
300 &self,
301 ids: &[String],
302 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
303 (&**self).bulk_restore(ids)
304 .await
305 .map_err(backbone_core::RepositoryError::from_source)
306 }
307
308 async fn bulk_hard_delete(
309 &self,
310 ids: &[String],
311 ) -> Result<u64, backbone_core::RepositoryError> {
312 (&**self).bulk_permanent_delete(ids)
313 .await
314 .map_err(backbone_core::RepositoryError::from_source)
315 }
316
317 async fn restore_all(&self) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
318 (&**self).restore_all()
319 .await
320 .map_err(backbone_core::RepositoryError::from_source)
321 }
322
323 async fn bulk_update(
324 &self,
325 entities: Vec<$entity>,
326 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
327 (&**self).bulk_update(&entities)
328 .await
329 .map_err(backbone_core::RepositoryError::from_source)
330 }
331 }
332 };
333
334 ($repo:ty, $entity:ty, no_soft_delete) => {
339 #[async_trait::async_trait]
340 impl backbone_core::CrudRepository<$entity> for $repo {
341 async fn fetch_related_json(
342 &self,
343 table: &str,
344 ids: &[String],
345 ) -> Vec<serde_json::Value> {
346 match backbone_orm::fetch_by_ids_as_json(
347 (&**self).pool(),
348 (&**self).table_name(),
349 table,
350 ids,
351 )
352 .await
353 {
354 Ok(rows) => rows,
355 Err(err) => {
356 backbone_core::log_include_hydration_failure(table, &err.to_string());
357 Vec::new()
358 }
359 }
360 }
361
362 async fn create(
363 &self,
364 entity: $entity,
365 ) -> Result<$entity, backbone_core::RepositoryError> {
366 (&**self).create(&entity)
367 .await
368 .map_err(backbone_core::RepositoryError::from_source)
369 }
370
371 async fn find_by_id(
372 &self,
373 id: &str,
374 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
375 (&**self).find_by_id(id)
376 .await
377 .map_err(backbone_core::RepositoryError::from_source)
378 }
379
380 async fn find_by_id_including_deleted(
381 &self,
382 id: &str,
383 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
384 (&**self).find_by_id(id)
386 .await
387 .map_err(backbone_core::RepositoryError::from_source)
388 }
389
390 async fn update(
391 &self,
392 entity: $entity,
393 ) -> Result<$entity, backbone_core::RepositoryError> {
394 let id = backbone_core::PersistentEntity::entity_id(&entity);
395 (&**self).update(&id, &entity)
396 .await
397 .map_err(backbone_core::RepositoryError::from_source)
398 .and_then(|opt| opt.ok_or(backbone_core::RepositoryError::NotFound))
399 }
400
401 async fn update_changed(
402 &self,
403 before: &$entity,
404 after: $entity,
405 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
406 let id = backbone_core::PersistentEntity::entity_id(&after);
407 (&**self)
408 .update_changed(&id, before, &after)
409 .await
410 .map_err(backbone_core::RepositoryError::from_source)
411 }
412
413 async fn soft_delete(
414 &self,
415 id: &str,
416 ) -> Result<bool, backbone_core::RepositoryError> {
417 (&**self).delete(id)
419 .await
420 .map_err(backbone_core::RepositoryError::from_source)
421 }
422
423 async fn restore(
424 &self,
425 id: &str,
426 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
427 (&**self).find_by_id(id)
429 .await
430 .map_err(backbone_core::RepositoryError::from_source)
431 }
432
433 async fn hard_delete(
434 &self,
435 id: &str,
436 ) -> Result<bool, backbone_core::RepositoryError> {
437 (&**self).delete(id)
438 .await
439 .map_err(backbone_core::RepositoryError::from_source)
440 }
441
442 async fn list(
443 &self,
444 page: u32,
445 limit: u32,
446 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
447 let pagination =
448 backbone_orm::repository::PaginationParams { page, per_page: limit };
449 (&**self).list_paginated(pagination)
450 .await
451 .map(|r| (r.data, r.pagination.total))
452 .map_err(backbone_core::RepositoryError::from_source)
453 }
454
455 async fn list_filtered(
456 &self,
457 page: u32,
458 limit: u32,
459 filters: std::collections::HashMap<String, String>,
460 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
461 let pagination =
462 backbone_orm::repository::PaginationParams { page, per_page: limit };
463 (&**self).list_paginated_filtered(pagination, Some(&filters))
464 .await
465 .map(|r| (r.data, r.pagination.total))
466 .map_err(backbone_core::RepositoryError::from_source)
467 }
468
469 async fn list_filtered_with_info(
470 &self,
471 page: u32,
472 limit: u32,
473 filters: std::collections::HashMap<String, String>,
474 ) -> Result<
475 (Vec<$entity>, backbone_orm::repository::PaginationInfo),
476 backbone_core::RepositoryError,
477 > {
478 let pagination =
479 backbone_orm::repository::PaginationParams { page, per_page: limit };
480 (&**self)
481 .list_paginated_filtered(pagination, Some(&filters))
482 .await
483 .map(|r| (r.data, r.pagination))
484 .map_err(backbone_core::RepositoryError::from_source)
485 }
486
487 fn table_name(&self) -> Option<&str> {
488 Some((&**self).table_name())
489 }
490
491 async fn aggregate_filtered(
492 &self,
493 spec: &backbone_orm::repository::AggregateSpec,
494 filters: std::collections::HashMap<String, String>,
495 ) -> Result<backbone_orm::repository::AggregateResult, backbone_core::RepositoryError> {
496 (&**self).aggregate_filtered(spec, Some(&filters))
497 .await
498 .map_err(backbone_core::RepositoryError::from_source)
499 }
500
501 async fn list_deleted(
502 &self,
503 _page: u32,
504 _limit: u32,
505 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
506 Ok((vec![], 0))
508 }
509
510 async fn count(&self) -> Result<u64, backbone_core::RepositoryError> {
511 (&**self).count()
512 .await
513 .map_err(backbone_core::RepositoryError::from_source)
514 }
515
516 async fn count_deleted(&self) -> Result<u64, backbone_core::RepositoryError> {
517 Ok(0)
518 }
519
520 async fn bulk_create(
521 &self,
522 entities: Vec<$entity>,
523 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
524 let mut results = Vec::with_capacity(entities.len());
525 for entity in entities {
526 let created = (&**self)
527 .create(&entity)
528 .await
529 .map_err(backbone_core::RepositoryError::from_source)?;
530 results.push(created);
531 }
532 Ok(results)
533 }
534
535 async fn empty_trash(&self) -> Result<u64, backbone_core::RepositoryError> {
536 Ok(0)
537 }
538
539 async fn bulk_soft_delete(
542 &self,
543 ids: &[String],
544 ) -> Result<u64, backbone_core::RepositoryError> {
545 (&**self).bulk_delete(ids)
547 .await
548 .map_err(backbone_core::RepositoryError::from_source)
549 }
550
551 async fn bulk_restore(
552 &self,
553 _ids: &[String],
554 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
555 Ok(Vec::new())
557 }
558
559 async fn bulk_hard_delete(
560 &self,
561 ids: &[String],
562 ) -> Result<u64, backbone_core::RepositoryError> {
563 (&**self).bulk_delete(ids)
564 .await
565 .map_err(backbone_core::RepositoryError::from_source)
566 }
567
568 async fn restore_all(&self) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
569 Ok(Vec::new())
571 }
572
573 async fn bulk_update(
574 &self,
575 entities: Vec<$entity>,
576 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
577 (&**self).bulk_update(&entities)
578 .await
579 .map_err(backbone_core::RepositoryError::from_source)
580 }
581 }
582 };
583}