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]
71macro_rules! impl_crud_repository {
72 ($repo:ty, $entity:ty, soft_delete) => {
78 #[async_trait::async_trait]
79 impl backbone_core::CrudRepository<$entity> for $repo {
80 async fn fetch_related_json(
81 &self,
82 table: &str,
83 ids: &[String],
84 ) -> Vec<serde_json::Value> {
85 match backbone_orm::fetch_by_ids_as_json(
86 (&**self).pool(),
87 (&**self).table_name(),
88 table,
89 ids,
90 )
91 .await
92 {
93 Ok(rows) => rows,
94 Err(err) => {
95 backbone_core::log_include_hydration_failure(table, &err.to_string());
96 Vec::new()
97 }
98 }
99 }
100
101 async fn reference_visible(
102 &self,
103 table: &str,
104 column: &str,
105 value: &serde_json::Value,
106 ) -> Result<Option<bool>, backbone_core::RepositoryError> {
107 backbone_orm::reference_visible(
108 (&**self).pool(),
109 (&**self).table_name(),
110 table,
111 column,
112 value,
113 )
114 .await
115 .map_err(backbone_core::RepositoryError::from_source)
116 }
117
118 async fn create(
126 &self,
127 entity: $entity,
128 ) -> Result<$entity, backbone_core::RepositoryError> {
129 (&**self).create(&entity)
130 .await
131 .map_err(backbone_core::RepositoryError::from_source)
132 }
133
134 async fn find_by_id(
135 &self,
136 id: &str,
137 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
138 (&**self).find_by_id(id)
139 .await
140 .map_err(backbone_core::RepositoryError::from_source)
141 }
142
143 async fn find_by_id_including_deleted(
144 &self,
145 id: &str,
146 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
147 (&**self).find_deleted_by_id(id)
148 .await
149 .map_err(backbone_core::RepositoryError::from_source)
150 }
151
152 async fn update(
153 &self,
154 entity: $entity,
155 ) -> Result<$entity, backbone_core::RepositoryError> {
156 let id = backbone_core::PersistentEntity::entity_id(&entity);
157 (&**self).update(&id, &entity)
158 .await
159 .map_err(backbone_core::RepositoryError::from_source)
160 .and_then(|opt| opt.ok_or(backbone_core::RepositoryError::NotFound))
161 }
162
163 async fn update_changed(
164 &self,
165 before: &$entity,
166 after: $entity,
167 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
168 let id = backbone_core::PersistentEntity::entity_id(&after);
169 (&**self)
170 .update_changed(&id, before, &after)
171 .await
172 .map_err(backbone_core::RepositoryError::from_source)
173 }
174
175 async fn soft_delete(
176 &self,
177 id: &str,
178 ) -> Result<bool, backbone_core::RepositoryError> {
179 (&**self).soft_delete(id)
180 .await
181 .map_err(backbone_core::RepositoryError::from_source)
182 }
183
184 async fn restore(
185 &self,
186 id: &str,
187 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
188 (&**self).restore(id)
189 .await
190 .map_err(backbone_core::RepositoryError::from_source)
191 }
192
193 async fn hard_delete(
194 &self,
195 id: &str,
196 ) -> Result<bool, backbone_core::RepositoryError> {
197 (&**self).permanent_delete(id)
198 .await
199 .map_err(backbone_core::RepositoryError::from_source)
200 }
201
202 async fn list(
203 &self,
204 page: u32,
205 limit: u32,
206 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
207 let pagination =
208 backbone_orm::repository::PaginationParams::new(page, limit);
209 (&**self).list_paginated(pagination)
210 .await
211 .map(|r| (r.data, r.pagination.total))
212 .map_err(backbone_core::RepositoryError::from_source)
213 }
214
215 async fn list_filtered(
216 &self,
217 page: u32,
218 limit: u32,
219 filters: std::collections::HashMap<String, String>,
220 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
221 let pagination =
222 backbone_orm::repository::PaginationParams::new(page, limit);
223 (&**self).list_paginated_filtered(pagination, Some(&filters))
224 .await
225 .map(|r| (r.data, r.pagination.total))
226 .map_err(backbone_core::RepositoryError::from_source)
227 }
228
229 async fn list_filtered_with_info(
230 &self,
231 page: u32,
232 limit: u32,
233 filters: std::collections::HashMap<String, String>,
234 ) -> Result<
235 (Vec<$entity>, backbone_orm::repository::PaginationInfo),
236 backbone_core::RepositoryError,
237 > {
238 let pagination =
239 backbone_orm::repository::PaginationParams::new(page, limit);
240 (&**self)
241 .list_paginated_filtered(pagination, Some(&filters))
242 .await
243 .map(|r| (r.data, r.pagination))
244 .map_err(backbone_core::RepositoryError::from_source)
245 }
246
247 fn table_name(&self) -> Option<&str> {
248 Some((&**self).table_name())
249 }
250
251 fn write_pool(&self) -> Option<&backbone_orm::PgPool> {
252 Some((&**self).pool())
253 }
254
255 async fn aggregate_filtered(
256 &self,
257 spec: &backbone_orm::repository::AggregateSpec,
258 filters: std::collections::HashMap<String, String>,
259 ) -> Result<backbone_orm::repository::AggregateResult, backbone_core::RepositoryError> {
260 (&**self).aggregate_filtered(spec, Some(&filters))
261 .await
262 .map_err(backbone_core::RepositoryError::from_source)
263 }
264
265 async fn list_deleted(
266 &self,
267 page: u32,
268 limit: u32,
269 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
270 let pagination =
271 backbone_orm::repository::PaginationParams::new(page, limit);
272 (&**self).list_deleted(pagination)
273 .await
274 .map(|r| (r.data, r.pagination.total))
275 .map_err(backbone_core::RepositoryError::from_source)
276 }
277
278 async fn list_deleted_filtered(
279 &self,
280 page: u32,
281 limit: u32,
282 filters: std::collections::HashMap<String, String>,
283 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
284 let pagination =
285 backbone_orm::repository::PaginationParams::new(page, limit);
286 (&**self).list_deleted_filtered(pagination, Some(&filters))
287 .await
288 .map(|r| (r.data, r.pagination.total))
289 .map_err(backbone_core::RepositoryError::from_source)
290 }
291
292 async fn count(&self) -> Result<u64, backbone_core::RepositoryError> {
293 (&**self).count_active()
294 .await
295 .map_err(backbone_core::RepositoryError::from_source)
296 }
297
298 async fn count_deleted(&self) -> Result<u64, backbone_core::RepositoryError> {
299 (&**self).count_deleted()
300 .await
301 .map_err(backbone_core::RepositoryError::from_source)
302 }
303
304 async fn bulk_create(
305 &self,
306 entities: Vec<$entity>,
307 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
308 (&**self).bulk_create(&entities)
310 .await
311 .map_err(backbone_core::RepositoryError::from_batch_source)
312 }
313
314 async fn empty_trash(&self) -> Result<u64, backbone_core::RepositoryError> {
315 (&**self).empty_trash()
316 .await
317 .map_err(backbone_core::RepositoryError::from_source)
318 }
319
320 async fn bulk_soft_delete(
323 &self,
324 ids: &[String],
325 ) -> Result<u64, backbone_core::RepositoryError> {
326 (&**self).bulk_soft_delete(ids)
327 .await
328 .map_err(backbone_core::RepositoryError::from_source)
329 }
330
331 async fn bulk_restore(
332 &self,
333 ids: &[String],
334 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
335 (&**self).bulk_restore(ids)
336 .await
337 .map_err(backbone_core::RepositoryError::from_source)
338 }
339
340 async fn bulk_hard_delete(
341 &self,
342 ids: &[String],
343 ) -> Result<u64, backbone_core::RepositoryError> {
344 (&**self).bulk_permanent_delete(ids)
345 .await
346 .map_err(backbone_core::RepositoryError::from_source)
347 }
348
349 async fn restore_all(&self) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
350 (&**self).restore_all()
351 .await
352 .map_err(backbone_core::RepositoryError::from_source)
353 }
354
355 async fn bulk_update(
356 &self,
357 entities: Vec<$entity>,
358 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
359 (&**self).bulk_update(&entities)
360 .await
361 .map_err(backbone_core::RepositoryError::from_source)
362 }
363
364 async fn bulk_update_changed(
365 &self,
366 pairs: Vec<($entity, $entity)>,
367 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
368 (&**self).bulk_update_changed(&pairs)
369 .await
370 .map_err(backbone_core::RepositoryError::from_batch_source)
371 }
372 }
373 };
374
375 ($repo:ty, $entity:ty, no_soft_delete) => {
380 #[async_trait::async_trait]
381 impl backbone_core::CrudRepository<$entity> for $repo {
382 async fn fetch_related_json(
383 &self,
384 table: &str,
385 ids: &[String],
386 ) -> Vec<serde_json::Value> {
387 match backbone_orm::fetch_by_ids_as_json(
388 (&**self).pool(),
389 (&**self).table_name(),
390 table,
391 ids,
392 )
393 .await
394 {
395 Ok(rows) => rows,
396 Err(err) => {
397 backbone_core::log_include_hydration_failure(table, &err.to_string());
398 Vec::new()
399 }
400 }
401 }
402
403 async fn reference_visible(
404 &self,
405 table: &str,
406 column: &str,
407 value: &serde_json::Value,
408 ) -> Result<Option<bool>, backbone_core::RepositoryError> {
409 backbone_orm::reference_visible(
410 (&**self).pool(),
411 (&**self).table_name(),
412 table,
413 column,
414 value,
415 )
416 .await
417 .map_err(backbone_core::RepositoryError::from_source)
418 }
419
420 async fn create(
421 &self,
422 entity: $entity,
423 ) -> Result<$entity, backbone_core::RepositoryError> {
424 (&**self).create(&entity)
425 .await
426 .map_err(backbone_core::RepositoryError::from_source)
427 }
428
429 async fn find_by_id(
430 &self,
431 id: &str,
432 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
433 (&**self).find_by_id(id)
434 .await
435 .map_err(backbone_core::RepositoryError::from_source)
436 }
437
438 async fn find_by_id_including_deleted(
439 &self,
440 id: &str,
441 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
442 (&**self).find_by_id(id)
444 .await
445 .map_err(backbone_core::RepositoryError::from_source)
446 }
447
448 async fn update(
449 &self,
450 entity: $entity,
451 ) -> Result<$entity, backbone_core::RepositoryError> {
452 let id = backbone_core::PersistentEntity::entity_id(&entity);
453 (&**self).update(&id, &entity)
454 .await
455 .map_err(backbone_core::RepositoryError::from_source)
456 .and_then(|opt| opt.ok_or(backbone_core::RepositoryError::NotFound))
457 }
458
459 async fn update_changed(
460 &self,
461 before: &$entity,
462 after: $entity,
463 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
464 let id = backbone_core::PersistentEntity::entity_id(&after);
465 (&**self)
466 .update_changed(&id, before, &after)
467 .await
468 .map_err(backbone_core::RepositoryError::from_source)
469 }
470
471 async fn soft_delete(
472 &self,
473 id: &str,
474 ) -> Result<bool, backbone_core::RepositoryError> {
475 (&**self).delete(id)
477 .await
478 .map_err(backbone_core::RepositoryError::from_source)
479 }
480
481 async fn restore(
482 &self,
483 id: &str,
484 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
485 (&**self).find_by_id(id)
487 .await
488 .map_err(backbone_core::RepositoryError::from_source)
489 }
490
491 async fn hard_delete(
492 &self,
493 id: &str,
494 ) -> Result<bool, backbone_core::RepositoryError> {
495 (&**self).delete(id)
496 .await
497 .map_err(backbone_core::RepositoryError::from_source)
498 }
499
500 async fn list(
501 &self,
502 page: u32,
503 limit: u32,
504 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
505 let pagination =
506 backbone_orm::repository::PaginationParams::new(page, limit);
507 (&**self).list_paginated(pagination)
508 .await
509 .map(|r| (r.data, r.pagination.total))
510 .map_err(backbone_core::RepositoryError::from_source)
511 }
512
513 async fn list_filtered(
514 &self,
515 page: u32,
516 limit: u32,
517 filters: std::collections::HashMap<String, String>,
518 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
519 let pagination =
520 backbone_orm::repository::PaginationParams::new(page, limit);
521 (&**self).list_paginated_filtered(pagination, Some(&filters))
522 .await
523 .map(|r| (r.data, r.pagination.total))
524 .map_err(backbone_core::RepositoryError::from_source)
525 }
526
527 async fn list_filtered_with_info(
528 &self,
529 page: u32,
530 limit: u32,
531 filters: std::collections::HashMap<String, String>,
532 ) -> Result<
533 (Vec<$entity>, backbone_orm::repository::PaginationInfo),
534 backbone_core::RepositoryError,
535 > {
536 let pagination =
537 backbone_orm::repository::PaginationParams::new(page, limit);
538 (&**self)
539 .list_paginated_filtered(pagination, Some(&filters))
540 .await
541 .map(|r| (r.data, r.pagination))
542 .map_err(backbone_core::RepositoryError::from_source)
543 }
544
545 fn table_name(&self) -> Option<&str> {
546 Some((&**self).table_name())
547 }
548
549 fn write_pool(&self) -> Option<&backbone_orm::PgPool> {
550 Some((&**self).pool())
551 }
552
553 async fn aggregate_filtered(
554 &self,
555 spec: &backbone_orm::repository::AggregateSpec,
556 filters: std::collections::HashMap<String, String>,
557 ) -> Result<backbone_orm::repository::AggregateResult, backbone_core::RepositoryError> {
558 (&**self).aggregate_filtered(spec, Some(&filters))
559 .await
560 .map_err(backbone_core::RepositoryError::from_source)
561 }
562
563 async fn list_deleted(
564 &self,
565 _page: u32,
566 _limit: u32,
567 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
568 Ok((vec![], 0))
570 }
571
572 async fn list_deleted_filtered(
573 &self,
574 _page: u32,
575 _limit: u32,
576 _filters: std::collections::HashMap<String, String>,
577 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
578 Ok((vec![], 0))
580 }
581
582 async fn count(&self) -> Result<u64, backbone_core::RepositoryError> {
583 (&**self).count()
584 .await
585 .map_err(backbone_core::RepositoryError::from_source)
586 }
587
588 async fn count_deleted(&self) -> Result<u64, backbone_core::RepositoryError> {
589 Ok(0)
590 }
591
592 async fn bulk_create(
593 &self,
594 entities: Vec<$entity>,
595 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
596 (&**self).bulk_create(&entities)
598 .await
599 .map_err(backbone_core::RepositoryError::from_batch_source)
600 }
601
602 async fn empty_trash(&self) -> Result<u64, backbone_core::RepositoryError> {
603 Ok(0)
604 }
605
606 async fn bulk_soft_delete(
609 &self,
610 ids: &[String],
611 ) -> Result<u64, backbone_core::RepositoryError> {
612 (&**self).bulk_delete(ids)
614 .await
615 .map_err(backbone_core::RepositoryError::from_source)
616 }
617
618 async fn bulk_restore(
619 &self,
620 _ids: &[String],
621 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
622 Ok(Vec::new())
624 }
625
626 async fn bulk_hard_delete(
627 &self,
628 ids: &[String],
629 ) -> Result<u64, backbone_core::RepositoryError> {
630 (&**self).bulk_delete(ids)
631 .await
632 .map_err(backbone_core::RepositoryError::from_source)
633 }
634
635 async fn restore_all(&self) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
636 Ok(Vec::new())
638 }
639
640 async fn bulk_update(
641 &self,
642 entities: Vec<$entity>,
643 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
644 (&**self).bulk_update(&entities)
645 .await
646 .map_err(backbone_core::RepositoryError::from_source)
647 }
648
649 async fn bulk_update_changed(
650 &self,
651 pairs: Vec<($entity, $entity)>,
652 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
653 (&**self).bulk_update_changed(&pairs)
654 .await
655 .map_err(backbone_core::RepositoryError::from_batch_source)
656 }
657 }
658 };
659}