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