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 async fn aggregate_filtered(
252 &self,
253 spec: &backbone_orm::repository::AggregateSpec,
254 filters: std::collections::HashMap<String, String>,
255 ) -> Result<backbone_orm::repository::AggregateResult, backbone_core::RepositoryError> {
256 (&**self).aggregate_filtered(spec, Some(&filters))
257 .await
258 .map_err(backbone_core::RepositoryError::from_source)
259 }
260
261 async fn list_deleted(
262 &self,
263 page: u32,
264 limit: u32,
265 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
266 let pagination =
267 backbone_orm::repository::PaginationParams::new(page, limit);
268 (&**self).list_deleted(pagination)
269 .await
270 .map(|r| (r.data, r.pagination.total))
271 .map_err(backbone_core::RepositoryError::from_source)
272 }
273
274 async fn list_deleted_filtered(
275 &self,
276 page: u32,
277 limit: u32,
278 filters: std::collections::HashMap<String, String>,
279 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
280 let pagination =
281 backbone_orm::repository::PaginationParams::new(page, limit);
282 (&**self).list_deleted_filtered(pagination, Some(&filters))
283 .await
284 .map(|r| (r.data, r.pagination.total))
285 .map_err(backbone_core::RepositoryError::from_source)
286 }
287
288 async fn count(&self) -> Result<u64, backbone_core::RepositoryError> {
289 (&**self).count_active()
290 .await
291 .map_err(backbone_core::RepositoryError::from_source)
292 }
293
294 async fn count_deleted(&self) -> Result<u64, backbone_core::RepositoryError> {
295 (&**self).count_deleted()
296 .await
297 .map_err(backbone_core::RepositoryError::from_source)
298 }
299
300 async fn bulk_create(
301 &self,
302 entities: Vec<$entity>,
303 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
304 (&**self).bulk_create(&entities)
306 .await
307 .map_err(backbone_core::RepositoryError::from_batch_source)
308 }
309
310 async fn empty_trash(&self) -> Result<u64, backbone_core::RepositoryError> {
311 (&**self).empty_trash()
312 .await
313 .map_err(backbone_core::RepositoryError::from_source)
314 }
315
316 async fn bulk_soft_delete(
319 &self,
320 ids: &[String],
321 ) -> Result<u64, backbone_core::RepositoryError> {
322 (&**self).bulk_soft_delete(ids)
323 .await
324 .map_err(backbone_core::RepositoryError::from_source)
325 }
326
327 async fn bulk_restore(
328 &self,
329 ids: &[String],
330 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
331 (&**self).bulk_restore(ids)
332 .await
333 .map_err(backbone_core::RepositoryError::from_source)
334 }
335
336 async fn bulk_hard_delete(
337 &self,
338 ids: &[String],
339 ) -> Result<u64, backbone_core::RepositoryError> {
340 (&**self).bulk_permanent_delete(ids)
341 .await
342 .map_err(backbone_core::RepositoryError::from_source)
343 }
344
345 async fn restore_all(&self) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
346 (&**self).restore_all()
347 .await
348 .map_err(backbone_core::RepositoryError::from_source)
349 }
350
351 async fn bulk_update(
352 &self,
353 entities: Vec<$entity>,
354 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
355 (&**self).bulk_update(&entities)
356 .await
357 .map_err(backbone_core::RepositoryError::from_source)
358 }
359
360 async fn bulk_update_changed(
361 &self,
362 pairs: Vec<($entity, $entity)>,
363 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
364 (&**self).bulk_update_changed(&pairs)
365 .await
366 .map_err(backbone_core::RepositoryError::from_batch_source)
367 }
368 }
369 };
370
371 ($repo:ty, $entity:ty, no_soft_delete) => {
376 #[async_trait::async_trait]
377 impl backbone_core::CrudRepository<$entity> for $repo {
378 async fn fetch_related_json(
379 &self,
380 table: &str,
381 ids: &[String],
382 ) -> Vec<serde_json::Value> {
383 match backbone_orm::fetch_by_ids_as_json(
384 (&**self).pool(),
385 (&**self).table_name(),
386 table,
387 ids,
388 )
389 .await
390 {
391 Ok(rows) => rows,
392 Err(err) => {
393 backbone_core::log_include_hydration_failure(table, &err.to_string());
394 Vec::new()
395 }
396 }
397 }
398
399 async fn reference_visible(
400 &self,
401 table: &str,
402 column: &str,
403 value: &serde_json::Value,
404 ) -> Result<Option<bool>, backbone_core::RepositoryError> {
405 backbone_orm::reference_visible(
406 (&**self).pool(),
407 (&**self).table_name(),
408 table,
409 column,
410 value,
411 )
412 .await
413 .map_err(backbone_core::RepositoryError::from_source)
414 }
415
416 async fn create(
417 &self,
418 entity: $entity,
419 ) -> Result<$entity, backbone_core::RepositoryError> {
420 (&**self).create(&entity)
421 .await
422 .map_err(backbone_core::RepositoryError::from_source)
423 }
424
425 async fn find_by_id(
426 &self,
427 id: &str,
428 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
429 (&**self).find_by_id(id)
430 .await
431 .map_err(backbone_core::RepositoryError::from_source)
432 }
433
434 async fn find_by_id_including_deleted(
435 &self,
436 id: &str,
437 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
438 (&**self).find_by_id(id)
440 .await
441 .map_err(backbone_core::RepositoryError::from_source)
442 }
443
444 async fn update(
445 &self,
446 entity: $entity,
447 ) -> Result<$entity, backbone_core::RepositoryError> {
448 let id = backbone_core::PersistentEntity::entity_id(&entity);
449 (&**self).update(&id, &entity)
450 .await
451 .map_err(backbone_core::RepositoryError::from_source)
452 .and_then(|opt| opt.ok_or(backbone_core::RepositoryError::NotFound))
453 }
454
455 async fn update_changed(
456 &self,
457 before: &$entity,
458 after: $entity,
459 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
460 let id = backbone_core::PersistentEntity::entity_id(&after);
461 (&**self)
462 .update_changed(&id, before, &after)
463 .await
464 .map_err(backbone_core::RepositoryError::from_source)
465 }
466
467 async fn soft_delete(
468 &self,
469 id: &str,
470 ) -> Result<bool, backbone_core::RepositoryError> {
471 (&**self).delete(id)
473 .await
474 .map_err(backbone_core::RepositoryError::from_source)
475 }
476
477 async fn restore(
478 &self,
479 id: &str,
480 ) -> Result<Option<$entity>, backbone_core::RepositoryError> {
481 (&**self).find_by_id(id)
483 .await
484 .map_err(backbone_core::RepositoryError::from_source)
485 }
486
487 async fn hard_delete(
488 &self,
489 id: &str,
490 ) -> Result<bool, backbone_core::RepositoryError> {
491 (&**self).delete(id)
492 .await
493 .map_err(backbone_core::RepositoryError::from_source)
494 }
495
496 async fn list(
497 &self,
498 page: u32,
499 limit: u32,
500 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
501 let pagination =
502 backbone_orm::repository::PaginationParams::new(page, limit);
503 (&**self).list_paginated(pagination)
504 .await
505 .map(|r| (r.data, r.pagination.total))
506 .map_err(backbone_core::RepositoryError::from_source)
507 }
508
509 async fn list_filtered(
510 &self,
511 page: u32,
512 limit: u32,
513 filters: std::collections::HashMap<String, String>,
514 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
515 let pagination =
516 backbone_orm::repository::PaginationParams::new(page, limit);
517 (&**self).list_paginated_filtered(pagination, Some(&filters))
518 .await
519 .map(|r| (r.data, r.pagination.total))
520 .map_err(backbone_core::RepositoryError::from_source)
521 }
522
523 async fn list_filtered_with_info(
524 &self,
525 page: u32,
526 limit: u32,
527 filters: std::collections::HashMap<String, String>,
528 ) -> Result<
529 (Vec<$entity>, backbone_orm::repository::PaginationInfo),
530 backbone_core::RepositoryError,
531 > {
532 let pagination =
533 backbone_orm::repository::PaginationParams::new(page, limit);
534 (&**self)
535 .list_paginated_filtered(pagination, Some(&filters))
536 .await
537 .map(|r| (r.data, r.pagination))
538 .map_err(backbone_core::RepositoryError::from_source)
539 }
540
541 fn table_name(&self) -> Option<&str> {
542 Some((&**self).table_name())
543 }
544
545 async fn aggregate_filtered(
546 &self,
547 spec: &backbone_orm::repository::AggregateSpec,
548 filters: std::collections::HashMap<String, String>,
549 ) -> Result<backbone_orm::repository::AggregateResult, backbone_core::RepositoryError> {
550 (&**self).aggregate_filtered(spec, Some(&filters))
551 .await
552 .map_err(backbone_core::RepositoryError::from_source)
553 }
554
555 async fn list_deleted(
556 &self,
557 _page: u32,
558 _limit: u32,
559 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
560 Ok((vec![], 0))
562 }
563
564 async fn list_deleted_filtered(
565 &self,
566 _page: u32,
567 _limit: u32,
568 _filters: std::collections::HashMap<String, String>,
569 ) -> Result<(Vec<$entity>, u64), backbone_core::RepositoryError> {
570 Ok((vec![], 0))
572 }
573
574 async fn count(&self) -> Result<u64, backbone_core::RepositoryError> {
575 (&**self).count()
576 .await
577 .map_err(backbone_core::RepositoryError::from_source)
578 }
579
580 async fn count_deleted(&self) -> Result<u64, backbone_core::RepositoryError> {
581 Ok(0)
582 }
583
584 async fn bulk_create(
585 &self,
586 entities: Vec<$entity>,
587 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
588 (&**self).bulk_create(&entities)
590 .await
591 .map_err(backbone_core::RepositoryError::from_batch_source)
592 }
593
594 async fn empty_trash(&self) -> Result<u64, backbone_core::RepositoryError> {
595 Ok(0)
596 }
597
598 async fn bulk_soft_delete(
601 &self,
602 ids: &[String],
603 ) -> Result<u64, backbone_core::RepositoryError> {
604 (&**self).bulk_delete(ids)
606 .await
607 .map_err(backbone_core::RepositoryError::from_source)
608 }
609
610 async fn bulk_restore(
611 &self,
612 _ids: &[String],
613 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
614 Ok(Vec::new())
616 }
617
618 async fn bulk_hard_delete(
619 &self,
620 ids: &[String],
621 ) -> Result<u64, backbone_core::RepositoryError> {
622 (&**self).bulk_delete(ids)
623 .await
624 .map_err(backbone_core::RepositoryError::from_source)
625 }
626
627 async fn restore_all(&self) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
628 Ok(Vec::new())
630 }
631
632 async fn bulk_update(
633 &self,
634 entities: Vec<$entity>,
635 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
636 (&**self).bulk_update(&entities)
637 .await
638 .map_err(backbone_core::RepositoryError::from_source)
639 }
640
641 async fn bulk_update_changed(
642 &self,
643 pairs: Vec<($entity, $entity)>,
644 ) -> Result<Vec<$entity>, backbone_core::RepositoryError> {
645 (&**self).bulk_update_changed(&pairs)
646 .await
647 .map_err(backbone_core::RepositoryError::from_batch_source)
648 }
649 }
650 };
651}