1use std::any::{Any, TypeId};
36use std::collections::HashMap;
37use std::fmt::Display;
38use std::str::FromStr;
39use std::sync::{Arc, Mutex, OnceLock};
40
41use crate::data::query::{
42 CursorData, DeleteQueryData, NoUser, PageResult, QueryData, RepositoryOptions,
43};
44use crate::data::types_extra::ChangeResultModel;
45use crate::data::{BaseAuditableEntity, BaseEntity};
46use chrono::Utc;
47use sea_orm::entity::prelude::DateTimeWithTimeZone;
48use sea_orm::sea_query::{Alias, Expr, ExprTrait};
49use sea_orm::{
50 ActiveModelTrait, ColumnTrait, DatabaseConnection, DatabaseTransaction, DbErr, EntityTrait,
51 IntoActiveModel, Iterable, ModelTrait, PaginatorTrait, PrimaryKeyToColumn, PrimaryKeyTrait,
52 QueryFilter, QuerySelect, TransactionTrait, Value, entity::EntityLoaderTrait,
53};
54use serde::Serialize;
55use uuid::Uuid;
56
57static REGISTRY: OnceLock<Mutex<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>> = OnceLock::new();
60
61fn registry() -> &'static Mutex<HashMap<TypeId, Arc<dyn Any + Send + Sync>>> {
62 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
63}
64
65fn with_cursor<E>(
69 select: sea_orm::Select<E>,
70 cursor: &Option<String>,
71 order_asc: Option<bool>,
72) -> sea_orm::Select<E>
73where
74 E: EntityTrait,
75{
76 if let Some(s) = cursor {
77 if let Some(data) = CursorData::decode(s) {
78 let is_asc = order_asc.unwrap_or(true);
79 return if is_asc {
80 select.filter(Expr::col(Alias::new("id")).gt(data.cursor))
81 } else {
82 select.filter(Expr::col(Alias::new("id")).lt(data.cursor))
83 };
84 }
85 }
86 select
87}
88
89fn with_cursor_delete<E>(
90 delete: sea_orm::DeleteMany<E>,
91 cursor: &Option<String>,
92) -> sea_orm::DeleteMany<E>
93where
94 E: EntityTrait,
95{
96 if let Some(s) = cursor {
97 if let Some(data) = CursorData::decode(s) {
98 return delete.filter(Expr::col(Alias::new("id")).gt(data.cursor));
99 }
100 }
101 delete
102}
103
104fn with_cursor_loader<E, L>(loader: L, cursor: &Option<String>, order_asc: Option<bool>) -> L
105where
106 E: EntityTrait,
107 L: EntityLoaderTrait<E>,
108{
109 if let Some(s) = cursor {
110 if let Some(data) = CursorData::decode(s) {
111 let is_asc = order_asc.unwrap_or(true);
112 return if is_asc {
113 loader.filter(Expr::col(Alias::new("id")).gt(data.cursor))
114 } else {
115 loader.filter(Expr::col(Alias::new("id")).lt(data.cursor))
116 };
117 }
118 }
119 loader
120}
121
122fn model_id<E, M>(model: &M) -> i64
123where
124 E: EntityTrait,
125 M: ModelTrait<Entity = E>,
126{
127 match model.get(E::PrimaryKey::iter().next().unwrap().into_column()) {
128 Value::BigInt(Some(id)) => id,
129 Value::Int(Some(id)) => i64::from(id),
130 value => panic!("repository cursor requires an integer primary key, got {value:?}"),
131 }
132}
133
134fn opts_txn<U>(opts: Option<&RepositoryOptions<U>>) -> Option<Arc<DatabaseTransaction>>
139where
140 U: BaseEntity + Clone + Send + Sync + 'static,
141{
142 opts.and_then(|o| o.txn.clone())
143}
144
145fn pick_txn<U>(
149 opts: Option<&RepositoryOptions<U>>,
150 qd_opts_txn: Option<Arc<DatabaseTransaction>>,
151) -> Option<Arc<DatabaseTransaction>>
152where
153 U: BaseEntity + Clone + Send + Sync + 'static,
154{
155 opts_txn(opts).or(qd_opts_txn)
156}
157
158fn try_set<A>(active: &mut A, col_name: &str, value: Value)
161where
162 A: ActiveModelTrait,
163 A::Entity: EntityTrait,
164 <A::Entity as EntityTrait>::Column: ColumnTrait + FromStr,
165 <<A::Entity as EntityTrait>::Column as FromStr>::Err: std::fmt::Debug,
166{
167 if let Ok(col) = <A::Entity as EntityTrait>::Column::from_str(col_name) {
168 active.set(col, value);
169 }
170}
171
172fn apply_base_create<A, U>(active: &mut A, opts: &RepositoryOptions<U>)
173where
174 A: ActiveModelTrait,
175 A::Entity: EntityTrait,
176 <A::Entity as EntityTrait>::Column: ColumnTrait + FromStr,
177 <<A::Entity as EntityTrait>::Column as FromStr>::Err: std::fmt::Debug,
178 U: BaseEntity + Clone + Send + Sync + 'static,
179{
180 let now: DateTimeWithTimeZone = Utc::now().into();
181 let uid = Uuid::new_v4();
182 try_set(active, "uid", Value::Uuid(Some(uid)));
184 try_set(active, "createdAt", Value::ChronoDateTimeWithTimeZone(Some(now)));
185 try_set(active, "updatedAt", Value::ChronoDateTimeWithTimeZone(Some(now)));
186 let user_id_opt = opts.user_id.or_else(|| opts.user.as_ref().map(|u| u.id()));
187 if let Some(uid) = user_id_opt {
188 try_set(active, "createdById", Value::BigInt(Some(uid)));
189 try_set(active, "updatedById", Value::BigInt(Some(uid)));
190 }
191}
192
193fn apply_base_update<A, U>(active: &mut A, opts: &RepositoryOptions<U>)
194where
195 A: ActiveModelTrait,
196 A::Entity: EntityTrait,
197 <A::Entity as EntityTrait>::Column: ColumnTrait + FromStr,
198 <<A::Entity as EntityTrait>::Column as FromStr>::Err: std::fmt::Debug,
199 U: BaseEntity + Clone + Send + Sync + 'static,
200{
201 let now: DateTimeWithTimeZone = Utc::now().into();
202 try_set(active, "updatedAt", Value::ChronoDateTimeWithTimeZone(Some(now)));
203 let user_id_opt = opts.user_id.or_else(|| opts.user.as_ref().map(|u| u.id()));
204 if let Some(uid) = user_id_opt {
205 try_set(active, "updatedById", Value::BigInt(Some(uid)));
206 }
207}
208
209#[derive(Clone)]
216pub struct PersistentRepository<E>
217where
218 E: EntityTrait,
219 E::ModelEx: BaseEntity + Send + Sync,
220 <E::ModelEx as BaseEntity>::LoaderType: EntityLoaderTrait<E, ModelEx = E::ModelEx>,
221{
222 pub db: DatabaseConnection,
224 _marker: std::marker::PhantomData<E>,
225}
226
227impl<E> PersistentRepository<E>
228where
229 E: EntityTrait,
230 E::ModelEx: BaseEntity + Send + Sync + Clone + Serialize,
231 E::Model: Into<E::ModelEx> + Send + Sync,
232 <E::ModelEx as BaseEntity>::LoaderType: EntityLoaderTrait<E, ModelEx = E::ModelEx>,
233 E::Column: ColumnTrait + FromStr,
234 <E::Column as FromStr>::Err: std::fmt::Debug,
235{
236 pub fn new(db: DatabaseConnection) -> Self {
238 Self {
239 db,
240 _marker: std::marker::PhantomData,
241 }
242 }
243
244 pub fn db(&self) -> &DatabaseConnection {
246 &self.db
247 }
248
249 pub fn initialize(db: &DatabaseConnection) -> Arc<Self> {
255 let type_id = TypeId::of::<E>();
256 let mut reg = registry().lock().unwrap();
257 if reg.contains_key(&type_id) {
258 panic!(
259 "Repository for {} has already been initialized",
260 std::any::type_name::<E>()
261 );
262 }
263 let repo = Arc::new(Self::new(db.clone()));
264 reg.insert(type_id, repo.clone() as Arc<dyn Any + Send + Sync>);
265 repo
266 }
267
268 pub fn find() -> Arc<Self> {
272 Self::try_find().unwrap_or_else(|| panic!("Repository for {} has not been initialized — call PersistentRepository::<{}>::initialize(db) first", std::any::type_name::<E>(), std::any::type_name::<E>()))
273 }
274
275 pub fn try_find() -> Option<Arc<Self>> {
277 let reg = registry().lock().unwrap();
278 reg.get(&TypeId::of::<E>())
279 .and_then(|arc| Arc::downcast::<Self>(arc.clone()).ok())
280 }
281
282 pub fn new_ephemeral(db: DatabaseConnection) -> Self {
284 Self::new(db)
285 }
286
287 pub fn start(&self) -> QueryData<E>
289 where
290 E::ModelEx: BaseEntity,
291 {
292 QueryData::new(<E::ModelEx as BaseEntity>::load())
293 }
294
295 pub fn start_delete(&self) -> DeleteQueryData<E> {
297 DeleteQueryData::new(E::delete_many())
298 }
299
300 pub fn is_owner<U>(&self, model: &E::ModelEx, user: &U) -> bool
302 where
303 U: BaseEntity,
304 {
305 model.id() == user.id()
306 }
307 pub fn is_owner_auditable<U>(&self, model: &E::ModelEx, user: &U) -> bool
309 where
310 U: BaseEntity + Clone + Send + Sync,
311 E::ModelEx: BaseAuditableEntity<User = U>,
312 {
313 model.created_by().id() == user.id()
314 }
315 pub fn is_accessible<U>(&self, model: &E::ModelEx, user: &U) -> bool
317 where
318 U: BaseEntity,
319 {
320 self.is_owner(model, user)
321 }
322 pub fn is_accessible_auditable<U>(&self, model: &E::ModelEx, user: &U) -> bool
324 where
325 U: BaseEntity + Clone + Send + Sync,
326 E::ModelEx: BaseAuditableEntity<User = U>,
327 {
328 model.created_by().id() == user.id() || model.updated_by().id() == user.id()
329 }
330
331 pub async fn transaction<F, T, EType>(&self, f: F) -> Result<T, EType>
342 where
343 EType: From<DbErr> + Display + Send,
344 F: FnOnce(
345 Arc<DatabaseTransaction>,
346 ) -> futures::future::BoxFuture<'static, Result<T, EType>>
347 + Send,
348 T: Send,
349 {
350 let txn = Arc::new(self.db.begin().await?);
351 let res = f(txn.clone()).await?;
352 let owned = Arc::try_unwrap(txn).map_err(|_| {
353 DbErr::Custom(
354 "transaction handle still shared after closure; refusing to commit".to_string(),
355 )
356 })?;
357 owned.commit().await?;
358 Ok(res)
359 }
360
361 pub async fn transaction_with_opts<U, F, T, EType>(
368 &self,
369 opts: RepositoryOptions<U>,
370 f: F,
371 ) -> Result<T, EType>
372 where
373 EType: From<DbErr> + Display + Send,
374 U: BaseEntity + Clone + Send + Sync + 'static,
375 F: FnOnce(
376 Arc<DatabaseTransaction>,
377 ) -> futures::future::BoxFuture<'static, Result<T, EType>>
378 + Send,
379 T: Send,
380 {
381 if let Some(txn) = opts.txn {
382 return f(txn).await;
383 }
384 self.transaction(f).await
385 }
386
387 pub async fn get_count<U>(
395 &self,
396 filter: Option<QueryData<E>>,
397 opts: Option<RepositoryOptions<U>>,
398 ) -> Result<u64, DbErr>
399 where
400 U: BaseEntity + Clone + Send + Sync + 'static,
401 E::ModelEx: BaseEntity,
402 {
403 let cursor = opts.as_ref().and_then(|o| o.cursor.clone());
404 let txn = pick_txn(
405 opts.as_ref(),
406 filter.as_ref().and_then(|qd| qd.opts.txn.clone()),
407 );
408 let (loader, query_cursor, order) = match filter {
409 Some(qd) => (qd.loader, qd.opts.cursor, qd.order_asc),
410 None => (<E::ModelEx as BaseEntity>::load(), None, None),
411 };
412 let loader = with_cursor_loader(loader, &cursor.or(query_cursor), order);
413 if let Some(txn) = txn.as_ref() {
414 loader.num_items(txn.as_ref(), 0).await
415 } else {
416 loader.num_items(&self.db, 0).await
417 }
418 }
419
420 pub async fn get_count_txn(
422 &self,
423 filter: Option<QueryData<E>>,
424 txn: &DatabaseTransaction,
425 ) -> Result<u64, DbErr>
426 where
427 E::ModelEx: BaseEntity,
428 {
429 let (loader, cursor, order) = match filter {
430 Some(qd) => (qd.loader, qd.opts.cursor, qd.order_asc),
431 None => (<E::ModelEx as BaseEntity>::load(), None, None),
432 };
433 with_cursor_loader(loader, &cursor, order)
434 .num_items(txn, 0)
435 .await
436 }
437
438 pub async fn count(&self, filter: Option<QueryData<E>>) -> Result<u64, DbErr>
440 where
441 E::ModelEx: BaseEntity,
442 {
443 self.get_count::<NoUser>(filter, None).await
444 }
445
446 pub async fn get_paginated_view<U>(
454 &self,
455 filter: Option<QueryData<E>>,
456 opts: RepositoryOptions<U>,
457 ) -> Result<PageResult<E::ModelEx>, DbErr>
458 where
459 U: BaseEntity + Clone + Send + Sync + 'static,
460 E::ModelEx: BaseEntity,
461 {
462 let order_asc = filter.as_ref().and_then(|qd| qd.order_asc);
463 let txn = opts
464 .txn
465 .clone()
466 .or_else(|| filter.as_ref().and_then(|qd| qd.opts.txn.clone()));
467 let loader = filter
468 .map(|qd| qd.loader)
469 .unwrap_or_else(<E::ModelEx as BaseEntity>::load);
470 let total_loader = loader.clone();
471 let total = if let Some(txn) = txn.as_ref() {
472 total_loader.num_items(txn.as_ref(), 0).await? as i64
473 } else {
474 total_loader.num_items(&self.db, 0).await? as i64
475 };
476
477 let sel = with_cursor_loader(loader, &opts.cursor, order_asc);
478 let limit = opts.limit;
479 let rows: Vec<E::ModelEx> = if let Some(txn) = txn.as_ref() {
480 sel.fetch(txn.as_ref(), 0, (limit + 1) as u64).await?
481 } else {
482 sel.fetch(&self.db, 0, (limit + 1) as u64).await?
483 };
484 let has_next = rows.len() > limit;
485 let mut data = rows;
486 if has_next {
487 data.truncate(limit);
488 }
489 let next_cursor = if has_next {
490 Some(
491 CursorData {
492 limit,
493 cursor: model_id::<E, _>(data.last().unwrap()),
494 }
495 .encode(),
496 )
497 } else {
498 None
499 };
500 Ok(PageResult {
501 data,
502 has_next,
503 cursor: opts.cursor.clone(),
504 next_cursor,
505 total,
506 limit,
507 })
508 }
509
510 pub async fn paginated(
516 &self,
517 select: sea_orm::Select<E>,
518 opts: RepositoryOptions<NoUser>,
519 ) -> Result<PageResult<E::ModelEx>, DbErr>
520 where
521 E::ModelEx: BaseEntity,
522 E::Model: Into<E::ModelEx> + Send + Sync,
523 {
524 let limit = opts.limit;
525 let cursor = opts.cursor.clone();
526 let txn = opts.txn.clone();
527 let mut count_sel = select.clone();
529 if opts.distinct {
530 count_sel = count_sel.distinct();
531 }
532 let total = if let Some(txn) = txn.as_ref() {
533 count_sel.paginate(txn.as_ref(), 1).num_items().await? as i64
534 } else {
535 count_sel.paginate(&self.db, 1).num_items().await? as i64
536 };
537
538 let mut sel = with_cursor(select, &cursor, None);
539 if opts.distinct {
540 sel = sel.distinct();
541 }
542 sel = sel.limit((limit + 1) as u64);
543 let rows: Vec<E::ModelEx> = if let Some(txn) = txn.as_ref() {
544 sel.all(txn.as_ref())
545 .await?
546 .into_iter()
547 .map(Into::into)
548 .collect()
549 } else {
550 sel.all(&self.db)
551 .await?
552 .into_iter()
553 .map(Into::into)
554 .collect()
555 };
556 let has_next = rows.len() > limit;
557 let mut data = rows;
558 if has_next {
559 data.truncate(limit);
560 }
561 let next_cursor = if has_next {
562 Some(
563 CursorData {
564 limit,
565 cursor: model_id::<E, _>(data.last().unwrap()),
566 }
567 .encode(),
568 )
569 } else {
570 None
571 };
572 Ok(PageResult {
573 data,
574 has_next,
575 cursor: opts.cursor,
576 next_cursor,
577 total,
578 limit: opts.limit,
579 })
580 }
581
582 pub async fn get_all<U>(
590 &self,
591 filter: Option<QueryData<E>>,
592 opts: Option<RepositoryOptions<U>>,
593 ) -> Result<Vec<E::ModelEx>, DbErr>
594 where
595 U: BaseEntity + Clone + Send + Sync + 'static,
596 E::ModelEx: BaseEntity,
597 {
598 let cursor = opts.as_ref().and_then(|o| o.cursor.clone());
599 let txn = pick_txn(
600 opts.as_ref(),
601 filter.as_ref().and_then(|qd| qd.opts.txn.clone()),
602 );
603 let (loader, qd_cursor, order) = match filter {
604 Some(qd) => (qd.loader, qd.opts.cursor, qd.order_asc),
605 None => (<E::ModelEx as BaseEntity>::load(), None, None),
606 };
607 let cursor = cursor.or(qd_cursor);
608 let loader = with_cursor_loader(loader, &cursor, order);
609 if let Some(txn) = txn.as_ref() {
610 loader.fetch(txn.as_ref(), 0, 0).await
611 } else {
612 loader.fetch(&self.db, 0, 0).await
613 }
614 }
615
616 pub async fn get_many<U>(
622 &self,
623 filter: Option<QueryData<E>>,
624 opts: RepositoryOptions<U>,
625 ) -> Result<Vec<E::ModelEx>, DbErr>
626 where
627 U: BaseEntity + Clone + Send + Sync + 'static,
628 E::ModelEx: BaseEntity,
629 {
630 let cursor = opts.cursor.clone();
631 let txn = opts
632 .txn
633 .clone()
634 .or_else(|| filter.as_ref().and_then(|qd| qd.opts.txn.clone()));
635 let (loader, qd_cursor, order) = match filter {
636 Some(qd) => (qd.loader, qd.opts.cursor, qd.order_asc),
637 None => (<E::ModelEx as BaseEntity>::load(), None, None),
638 };
639 let cursor = if cursor.is_some() { cursor } else { qd_cursor };
640 let loader = with_cursor_loader(loader, &cursor, order);
641 if let Some(txn) = txn.as_ref() {
642 loader.fetch(txn.as_ref(), 0, opts.limit as u64).await
643 } else {
644 loader.fetch(&self.db, 0, opts.limit as u64).await
645 }
646 }
647
648 pub async fn find_many<U>(
650 &self,
651 filter: Option<QueryData<E>>,
652 opts: RepositoryOptions<U>,
653 ) -> Result<Vec<E::ModelEx>, DbErr>
654 where
655 U: BaseEntity + Clone + Send + Sync + 'static,
656 E::ModelEx: BaseEntity,
657 {
658 self.get_many(filter, opts).await
659 }
660
661 pub async fn find_all(&self, filter: Option<QueryData<E>>) -> Result<Vec<E::ModelEx>, DbErr>
663 where
664 E::ModelEx: BaseEntity,
665 {
666 self.get_all::<NoUser>(filter, None).await
667 }
668
669 pub async fn get_distinct_rows<U>(
674 &self,
675 filter: Option<QueryData<E>>,
676 opts: RepositoryOptions<U>,
677 ) -> Result<Vec<E::ModelEx>, DbErr>
678 where
679 U: BaseEntity + Clone + Send + Sync + 'static,
680 E::ModelEx: BaseEntity,
681 {
682 let cursor = opts.cursor.clone();
683 let txn = opts
684 .txn
685 .clone()
686 .or_else(|| filter.as_ref().and_then(|qd| qd.opts.txn.clone()));
687 let (loader, qd_cursor, order) = match filter {
688 Some(qd) => (qd.loader, qd.opts.cursor, qd.order_asc),
689 None => (<E::ModelEx as BaseEntity>::load(), None, None),
690 };
691 let loader = with_cursor_loader(loader, &cursor.or(qd_cursor), order);
692 if let Some(txn) = txn.as_ref() {
693 loader.fetch(txn.as_ref(), 0, opts.limit as u64).await
694 } else {
695 loader.fetch(&self.db, 0, opts.limit as u64).await
696 }
697 }
698
699 pub async fn get_one<U>(
704 &self,
705 filter: Option<QueryData<E>>,
706 opts: Option<RepositoryOptions<U>>,
707 ) -> Result<Option<E::ModelEx>, DbErr>
708 where
709 U: BaseEntity + Clone + Send + Sync + 'static,
710 E::ModelEx: BaseEntity,
711 {
712 let cursor = opts.as_ref().and_then(|o| o.cursor.clone());
713 let txn = pick_txn(
714 opts.as_ref(),
715 filter.as_ref().and_then(|qd| qd.opts.txn.clone()),
716 );
717 let (loader, qd_cursor, order) = match filter {
718 Some(qd) => (qd.loader, qd.opts.cursor, qd.order_asc),
719 None => (<E::ModelEx as BaseEntity>::load(), None, None),
720 };
721 let loader = with_cursor_loader(loader, &cursor.or(qd_cursor), order);
722 if let Some(txn) = txn.as_ref() {
723 Ok(loader.fetch(txn.as_ref(), 0, 1).await?.into_iter().next())
724 } else {
725 Ok(loader.fetch(&self.db, 0, 1).await?.into_iter().next())
726 }
727 }
728
729 pub async fn find_one(&self, filter: Option<QueryData<E>>) -> Result<Option<E::ModelEx>, DbErr>
731 where
732 E::ModelEx: BaseEntity,
733 {
734 self.get_one::<NoUser>(filter, None).await
735 }
736
737 pub async fn get_by_id(&self, id: i64) -> Result<Option<E::ModelEx>, DbErr>
739 where
740 E::PrimaryKey: PrimaryKeyTrait,
741 <E::PrimaryKey as PrimaryKeyTrait>::ValueType: From<i64>,
742 E::ModelEx: BaseEntity,
743 <E::ModelEx as BaseEntity>::LoaderType: EntityLoaderTrait<E, ModelEx = E::ModelEx>,
744 {
745 <E::ModelEx as BaseEntity>::load()
746 .filter_by_id(id)
747 .fetch(&self.db, 0, 1)
748 .await
749 .map(|mut rows| rows.pop())
750 }
751
752 pub async fn find_by_id(&self, id: i64) -> Result<Option<E::ModelEx>, DbErr>
754 where
755 E::PrimaryKey: PrimaryKeyTrait,
756 <E::PrimaryKey as PrimaryKeyTrait>::ValueType: From<i64>,
757 E::ModelEx: BaseEntity,
758 {
759 self.get_by_id(id).await
760 }
761
762 pub async fn get_by_id_with_opts<U>(
766 &self,
767 id: i64,
768 opts: RepositoryOptions<U>,
769 ) -> Result<Option<E::ModelEx>, DbErr>
770 where
771 U: BaseEntity + Clone + Send + Sync + 'static,
772 E::PrimaryKey: PrimaryKeyTrait,
773 <E::PrimaryKey as PrimaryKeyTrait>::ValueType: From<i64>,
774 E::ModelEx: BaseEntity,
775 {
776 let loader = <E::ModelEx as BaseEntity>::load().filter_by_id(id);
777 if let Some(txn) = opts.txn.as_ref() {
778 Ok(loader.fetch(txn.as_ref(), 0, 1).await?.into_iter().next())
779 } else {
780 Ok(loader.fetch(&self.db, 0, 1).await?.into_iter().next())
781 }
782 }
783
784 pub async fn create_one<A, U>(
791 &self,
792 mut active: A,
793 opts: Option<RepositoryOptions<U>>,
794 ) -> Result<E::ModelEx, DbErr>
795 where
796 A: ActiveModelTrait<Entity = E> + Send,
797 A: sea_orm::ActiveModelBehavior + Send,
798 E::Model: IntoActiveModel<A>,
799 E::ModelEx: BaseEntity,
800 U: BaseEntity + Clone + Send + Sync + 'static,
801 E::Column: ColumnTrait + FromStr,
802 <E::Column as FromStr>::Err: std::fmt::Debug,
803 {
804 if let Some(o) = opts.as_ref() {
805 apply_base_create(&mut active, o);
806 } else {
807 let empty: RepositoryOptions<U> = RepositoryOptions::default();
808 apply_base_create(&mut active, &empty);
809 }
810 if let Some(txn) = opts_txn(opts.as_ref()).as_ref() {
811 active.insert(txn.as_ref()).await.map(Into::into)
812 } else {
813 active.insert(&self.db).await.map(Into::into)
814 }
815 }
816
817 pub async fn create_one_simple<A>(&self, active: A) -> Result<E::ModelEx, DbErr>
820 where
821 A: ActiveModelTrait<Entity = E> + Send,
822 A: sea_orm::ActiveModelBehavior + Send,
823 E::Model: IntoActiveModel<A>,
824 E::ModelEx: BaseEntity,
825 E::Column: ColumnTrait + FromStr,
826 <E::Column as FromStr>::Err: std::fmt::Debug,
827 {
828 self.create_one::<A, NoUser>(active, None).await
829 }
830
831 pub async fn create_one_with_opts<A, U>(
835 &self,
836 active: A,
837 opts: RepositoryOptions<U>,
838 ) -> Result<E::ModelEx, DbErr>
839 where
840 A: ActiveModelTrait<Entity = E> + Send,
841 A: sea_orm::ActiveModelBehavior + Send,
842 E::Model: IntoActiveModel<A>,
843 E::ModelEx: BaseEntity,
844 U: BaseEntity + Clone + Send + Sync + 'static,
845 E::Column: ColumnTrait + FromStr,
846 <E::Column as FromStr>::Err: std::fmt::Debug,
847 {
848 self.create_one(active, Some(opts)).await
849 }
850
851 pub async fn create_many<A, U>(
856 &self,
857 mut actives: Vec<A>,
858 opts: Option<RepositoryOptions<U>>,
859 ) -> Result<Vec<E::ModelEx>, DbErr>
860 where
861 A: ActiveModelTrait<Entity = E> + Send,
862 A: sea_orm::ActiveModelBehavior + Send,
863 E::ModelEx: BaseEntity,
864 E::Model: IntoActiveModel<A>,
865 U: BaseEntity + Clone + Send + Sync + 'static,
866 E::Column: ColumnTrait + FromStr,
867 <E::Column as FromStr>::Err: std::fmt::Debug,
868 {
869 let mut out = Vec::with_capacity(actives.len());
870 for active in &mut actives {
871 if let Some(o) = opts.as_ref() {
872 apply_base_create(active, o);
873 } else {
874 let empty: RepositoryOptions<U> = RepositoryOptions::default();
875 apply_base_create(active, &empty);
876 }
877 }
878 let txn = opts_txn(opts.as_ref());
879 for a in actives {
880 if let Some(txn) = txn.as_ref() {
881 out.push(a.insert(txn.as_ref()).await?.into());
882 } else {
883 out.push(a.insert(&self.db).await?.into());
884 }
885 }
886 Ok(out)
887 }
888
889 pub async fn create_many_simple<A>(&self, actives: Vec<A>) -> Result<Vec<E::ModelEx>, DbErr>
891 where
892 A: ActiveModelTrait<Entity = E> + Send,
893 A: sea_orm::ActiveModelBehavior + Send,
894 E::ModelEx: BaseEntity,
895 E::Model: IntoActiveModel<A>,
896 E::Column: ColumnTrait + FromStr,
897 <E::Column as FromStr>::Err: std::fmt::Debug,
898 {
899 self.create_many::<A, NoUser>(actives, None).await
900 }
901
902 pub async fn create_many_with_opts<A, U>(
904 &self,
905 actives: Vec<A>,
906 opts: RepositoryOptions<U>,
907 ) -> Result<Vec<E::ModelEx>, DbErr>
908 where
909 A: ActiveModelTrait<Entity = E> + Send,
910 A: sea_orm::ActiveModelBehavior + Send,
911 E::ModelEx: BaseEntity,
912 E::Model: IntoActiveModel<A>,
913 U: BaseEntity + Clone + Send + Sync + 'static,
914 E::Column: ColumnTrait + FromStr,
915 <E::Column as FromStr>::Err: std::fmt::Debug,
916 {
917 self.create_many(actives, Some(opts)).await
918 }
919
920 pub async fn upsert_one<A>(&self, mut active: A) -> Result<E::ModelEx, DbErr>
924 where
925 A: ActiveModelTrait<Entity = E> + Send,
926 A: sea_orm::ActiveModelBehavior + Send,
927 E::Model: IntoActiveModel<A> + Clone,
928 E::Column: ColumnTrait + FromStr,
929 <E::Column as FromStr>::Err: std::fmt::Debug,
930 {
931 let empty: RepositoryOptions<NoUser> = RepositoryOptions::default();
932 apply_base_create(&mut active, &empty);
933 active.insert(&self.db).await.map(Into::into)
934 }
935
936 pub async fn upsert_many<A>(&self, actives: Vec<A>) -> Result<Vec<E::ModelEx>, DbErr>
938 where
939 A: ActiveModelTrait<Entity = E> + Send,
940 A: sea_orm::ActiveModelBehavior + Send,
941 E::ModelEx: BaseEntity,
942 E::Model: IntoActiveModel<A> + Clone,
943 E::Column: ColumnTrait + FromStr,
944 <E::Column as FromStr>::Err: std::fmt::Debug,
945 {
946 let mut out = Vec::with_capacity(actives.len());
947 for a in actives {
948 out.push(self.upsert_one(a).await?);
949 }
950 Ok(out)
951 }
952
953 pub async fn upsert_one_with_id<A, F>(
957 &self,
958 mut active: A,
959 id_fn: F,
960 ) -> Result<E::ModelEx, DbErr>
961 where
962 A: ActiveModelTrait<Entity = E> + Send,
963 A: sea_orm::ActiveModelBehavior + Send,
964 E::Model: IntoActiveModel<A> + Clone + BaseEntity,
965 F: Fn(&A) -> Option<i64> + Send,
966 E::PrimaryKey: PrimaryKeyTrait,
967 <E::PrimaryKey as PrimaryKeyTrait>::ValueType: From<i64>,
968 E::Column: ColumnTrait + FromStr,
969 <E::Column as FromStr>::Err: std::fmt::Debug,
970 {
971 if let Some(id) = id_fn(&active) {
972 if self.get_by_id(id).await?.is_some() {
973 let empty: RepositoryOptions<NoUser> = RepositoryOptions::default();
975 apply_base_update(&mut active, &empty);
976 return active.update(&self.db).await.map(Into::into);
977 }
978 }
979 let empty: RepositoryOptions<NoUser> = RepositoryOptions::default();
980 apply_base_create(&mut active, &empty);
981 active.insert(&self.db).await.map(Into::into)
982 }
983
984 pub async fn update_many<F, U>(
993 &self,
994 filter: Option<QueryData<E>>,
995 mut effector: F,
996 opts: RepositoryOptions<U>,
997 ) -> Result<ChangeResultModel<E::ModelEx>, DbErr>
998 where
999 F: FnMut(&mut E::Model) -> bool + Send,
1000 U: BaseEntity + Clone + Send + Sync + 'static,
1001 E::ModelEx: Into<E::Model> + BaseEntity,
1002 E::Model: IntoActiveModel<E::ActiveModel> + Clone,
1003 E::ActiveModel: ActiveModelTrait<Entity = E> + Send,
1004 E::Column: ColumnTrait + FromStr,
1005 <E::Column as FromStr>::Err: std::fmt::Debug,
1006 {
1007 let txn = opts.txn.clone();
1008 let items = self.get_many(filter, opts.clone()).await?;
1009 let mut affected = Vec::new();
1010 for model in items {
1011 let mut model: E::Model = model.into();
1012 if effector(&mut model) {
1013 let mut am: E::ActiveModel = model.clone().into_active_model();
1014 apply_base_update(&mut am, &opts);
1015 if let Some(txn) = txn.as_ref() {
1016 am.update(txn.as_ref()).await?;
1017 } else {
1018 am.update(&self.db).await?;
1019 }
1020 affected.push(model.into());
1021 }
1022 }
1023 Ok(ChangeResultModel::new(affected))
1024 }
1025
1026 pub async fn find_update_many<F, U>(
1028 &self,
1029 filter: Option<QueryData<E>>,
1030 effector: F,
1031 opts: RepositoryOptions<U>,
1032 ) -> Result<ChangeResultModel<E::ModelEx>, DbErr>
1033 where
1034 F: FnMut(&mut E::Model) -> bool + Send,
1035 U: BaseEntity + Clone + Send + Sync + 'static,
1036 E::ModelEx: Into<E::Model> + BaseEntity,
1037 E::Model: IntoActiveModel<E::ActiveModel> + Clone,
1038 E::ActiveModel: ActiveModelTrait<Entity = E> + Send,
1039 E::Column: ColumnTrait + FromStr,
1040 <E::Column as FromStr>::Err: std::fmt::Debug,
1041 {
1042 self.update_many(filter, effector, opts).await
1043 }
1044
1045 pub async fn update_one<F>(
1050 &self,
1051 filter: Option<QueryData<E>>,
1052 mut effector: F,
1053 ) -> Result<Option<E::ModelEx>, DbErr>
1054 where
1055 F: FnMut(&mut E::Model) -> bool + Send,
1056 E::ModelEx: Into<E::Model> + BaseEntity,
1057 E::Model: IntoActiveModel<E::ActiveModel> + Clone ,
1058 E::ActiveModel: ActiveModelTrait<Entity = E> + Send,
1059 E::Column: ColumnTrait + FromStr,
1060 <E::Column as FromStr>::Err: std::fmt::Debug,
1061 {
1062 let Some(model) = self.get_one::<NoUser>(filter, None).await? else {
1063 return Ok(None);
1064 };
1065 let mut model: E::Model = model.into();
1066 if !effector(&mut model) {
1067 return Ok(Some(model.into()));
1068 }
1069 let mut am: E::ActiveModel = model.clone().into_active_model();
1070 let empty: RepositoryOptions<NoUser> = RepositoryOptions::default();
1071 apply_base_update(&mut am, &empty);
1072 let updated = am.update(&self.db).await?;
1073 Ok(Some(updated.into()))
1074 }
1075
1076 pub async fn update_by_id<F>(
1080 &self,
1081 id: i64,
1082 mut effector: F,
1083 ) -> Result<Option<E::ModelEx>, DbErr>
1084 where
1085 F: FnMut(&mut E::Model) -> bool + Send,
1086 E::ModelEx: Into<E::Model> + BaseEntity,
1087 E::Model: IntoActiveModel<E::ActiveModel> + Clone,
1088 E::ActiveModel: ActiveModelTrait<Entity = E> + Send,
1089 E::PrimaryKey: PrimaryKeyTrait,
1090 <E::PrimaryKey as PrimaryKeyTrait>::ValueType: From<i64>,
1091 E::Column: ColumnTrait + FromStr,
1092 <E::Column as FromStr>::Err: std::fmt::Debug,
1093 {
1094 let Some(model) = self.get_by_id(id).await? else {
1095 return Ok(None);
1096 };
1097 let mut model: E::Model = model.into();
1098 if !effector(&mut model) {
1099 return Ok(Some(model.into()));
1100 }
1101 let mut am: E::ActiveModel = model.clone().into_active_model();
1102 let empty: RepositoryOptions<NoUser> = RepositoryOptions::default();
1103 apply_base_update(&mut am, &empty);
1104 let updated = am.update(&self.db).await?;
1105 Ok(Some(updated.into()))
1106 }
1107
1108 pub async fn update_by_id_with_active<F>(
1113 &self,
1114 id: i64,
1115 mut effector: F,
1116 ) -> Result<Option<E::ModelEx>, DbErr>
1117 where
1118 F: FnMut(&mut E::ActiveModel) -> bool + Send,
1119 E::ModelEx: Into<E::Model> + BaseEntity,
1120 E::Model: IntoActiveModel<E::ActiveModel> + Clone,
1121 E::ActiveModel: ActiveModelTrait<Entity = E> + Send,
1122 E::PrimaryKey: PrimaryKeyTrait,
1123 <E::PrimaryKey as PrimaryKeyTrait>::ValueType: From<i64>,
1124 E::Column: ColumnTrait + FromStr,
1125 <E::Column as FromStr>::Err: std::fmt::Debug,
1126 {
1127 let Some(model) = self.get_by_id(id).await? else {
1128 return Ok(None);
1129 };
1130 let model: E::Model = model.into();
1131 let mut am: E::ActiveModel = model.into_active_model();
1132 if !effector(&mut am) {
1133 return self.get_by_id(id).await;
1134 }
1135 let empty: RepositoryOptions<NoUser> = RepositoryOptions::default();
1136 apply_base_update(&mut am, &empty);
1137 let updated = am.update(&self.db).await?;
1138 Ok(Some(updated.into()))
1139 }
1140
1141 pub async fn update_one_with_opts<F, U>(
1146 &self,
1147 filter: Option<QueryData<E>>,
1148 mut effector: F,
1149 opts: Option<RepositoryOptions<U>>,
1150 ) -> Result<Option<E::ModelEx>, DbErr>
1151 where
1152 F: FnMut(&mut E::Model) -> bool + Send,
1153 U: BaseEntity + Clone + Send + Sync + 'static,
1154 E::ModelEx: Into<E::Model>,
1155 E::Model: IntoActiveModel<E::ActiveModel> + Clone,
1156 E::ActiveModel: ActiveModelTrait<Entity = E> + Send,
1157 E::Column: ColumnTrait + FromStr,
1158 <E::Column as FromStr>::Err: std::fmt::Debug,
1159 {
1160 let txn = pick_txn(
1161 opts.as_ref(),
1162 filter.as_ref().and_then(|qd| qd.opts.txn.clone()),
1163 );
1164 let Some(model) = self.get_one(filter, opts.clone()).await? else {
1165 return Ok(None);
1166 };
1167 let mut model: E::Model = model.into();
1168 if !effector(&mut model) {
1169 return Ok(Some(model.into()));
1170 }
1171 let mut am: E::ActiveModel = model.clone().into_active_model();
1172 if let Some(o) = opts.as_ref() {
1173 apply_base_update(&mut am, o);
1174 } else {
1175 let empty: RepositoryOptions<NoUser> = RepositoryOptions::default();
1176 apply_base_update(&mut am, &empty);
1177 }
1178 let updated = if let Some(txn) = txn.as_ref() {
1179 am.update(txn.as_ref()).await?
1180 } else {
1181 am.update(&self.db).await?
1182 };
1183 Ok(Some(updated.into()))
1184 }
1185
1186 pub async fn update_by_id_with_opts<F, U>(
1191 &self,
1192 id: i64,
1193 mut effector: F,
1194 opts: Option<RepositoryOptions<U>>,
1195 ) -> Result<Option<E::ModelEx>, DbErr>
1196 where
1197 F: FnMut(&mut E::Model) -> bool + Send,
1198 U: BaseEntity + Clone + Send + Sync + 'static,
1199 E::ModelEx: Into<E::Model> + BaseEntity,
1200 E::Model: IntoActiveModel<E::ActiveModel> + Clone,
1201 E::ActiveModel: ActiveModelTrait<Entity = E> + Send,
1202 E::PrimaryKey: PrimaryKeyTrait,
1203 <E::PrimaryKey as PrimaryKeyTrait>::ValueType: From<i64>,
1204 E::Column: ColumnTrait + FromStr,
1205 <E::Column as FromStr>::Err: std::fmt::Debug,
1206 {
1207 let txn = opts_txn(opts.as_ref());
1208 let Some(model) = (if let Some(o) = opts.clone() {
1209 self.get_by_id_with_opts(id, o).await?
1211 } else {
1212 self.get_by_id(id).await?
1213 }) else {
1214 return Ok(None);
1215 };
1216 let mut model: E::Model = model.into();
1217 if !effector(&mut model) {
1218 return Ok(Some(model.into()));
1219 }
1220 let mut am: E::ActiveModel = model.clone().into_active_model();
1221 if let Some(o) = opts.as_ref() {
1222 apply_base_update(&mut am, o);
1223 } else {
1224 let empty: RepositoryOptions<NoUser> = RepositoryOptions::default();
1225 apply_base_update(&mut am, &empty);
1226 }
1227 let updated = if let Some(txn) = txn.as_ref() {
1228 am.update(txn.as_ref()).await?
1229 } else {
1230 am.update(&self.db).await?
1231 };
1232 Ok(Some(updated.into()))
1233 }
1234
1235 pub async fn delete_many(&self, filter: Option<DeleteQueryData<E>>) -> Result<u64, DbErr> {
1239 self.delete_many_with_opts::<NoUser>(filter, None).await
1240 }
1241 pub async fn delete_many_with_opts<U>(
1246 &self,
1247 filter: Option<DeleteQueryData<E>>,
1248 opts: Option<RepositoryOptions<U>>,
1249 ) -> Result<u64, DbErr>
1250 where
1251 U: BaseEntity + Clone + Send + Sync + 'static,
1252 {
1253 let cursor = opts.as_ref().and_then(|o| o.cursor.clone());
1254 let txn = opts_txn(opts.as_ref());
1255 let del = filter
1256 .map(|qd| qd.delete)
1257 .unwrap_or_else(|| E::delete_many());
1258 let del = with_cursor_delete(del, &cursor);
1259 let res = if let Some(txn) = txn.as_ref() {
1260 del.exec(txn.as_ref()).await?
1261 } else {
1262 del.exec(&self.db).await?
1263 };
1264 Ok(res.rows_affected)
1265 }
1266 pub async fn remove_many(&self, filter: Option<DeleteQueryData<E>>) -> Result<u64, DbErr> {
1268 self.delete_many(filter).await
1269 }
1270 pub async fn remove_many_with_opts<U>(
1272 &self,
1273 filter: Option<DeleteQueryData<E>>,
1274 opts: Option<RepositoryOptions<U>>,
1275 ) -> Result<u64, DbErr>
1276 where
1277 U: BaseEntity + Clone + Send + Sync + 'static,
1278 {
1279 self.delete_many_with_opts(filter, opts).await
1280 }
1281 pub async fn delete_one(&self, filter: Option<DeleteQueryData<E>>) -> Result<bool, DbErr> {
1283 self.delete_one_with_opts::<NoUser>(filter, None).await
1284 }
1285 pub async fn delete_one_with_opts<U>(
1290 &self,
1291 filter: Option<DeleteQueryData<E>>,
1292 opts: Option<RepositoryOptions<U>>,
1293 ) -> Result<bool, DbErr>
1294 where
1295 U: BaseEntity + Clone + Send + Sync + 'static,
1296 {
1297 Ok(self.delete_many_with_opts(filter, opts).await? > 0)
1298 }
1299
1300 pub async fn delete_by_id(&self, id: i64) -> Result<Option<E::ModelEx>, DbErr>
1302 where
1303 E::PrimaryKey: PrimaryKeyTrait,
1304 <E::PrimaryKey as PrimaryKeyTrait>::ValueType: From<i64>,
1305 E::ModelEx: BaseEntity,
1306 {
1307 let Some(model) = self.get_by_id(id).await? else {
1308 return Ok(None);
1309 };
1310 let pk = <<E::PrimaryKey as PrimaryKeyTrait>::ValueType as From<i64>>::from(id);
1311 E::delete_by_id(pk).exec(&self.db).await?;
1312 Ok(Some(model))
1313 }
1314
1315 pub async fn delete_by_id_with_opts<U>(
1320 &self,
1321 id: i64,
1322 opts: Option<RepositoryOptions<U>>,
1323 ) -> Result<Option<E::ModelEx>, DbErr>
1324 where
1325 U: BaseEntity + Clone + Send + Sync + 'static,
1326 E::PrimaryKey: PrimaryKeyTrait,
1327 <E::PrimaryKey as PrimaryKeyTrait>::ValueType: From<i64>,
1328 E::ModelEx: BaseEntity,
1329 {
1330 let txn = opts_txn(opts.as_ref());
1331 let Some(model) = (if let Some(txn) = txn.as_ref() {
1332 <E::ModelEx as BaseEntity>::load()
1333 .filter_by_id(id)
1334 .fetch(txn.as_ref(), 0, 1)
1335 .await?
1336 .into_iter()
1337 .next()
1338 } else {
1339 self.get_by_id(id).await?
1340 }) else {
1341 return Ok(None);
1342 };
1343 let pk = <<E::PrimaryKey as PrimaryKeyTrait>::ValueType as From<i64>>::from(id);
1344 if let Some(txn) = txn.as_ref() {
1345 E::delete_by_id(pk).exec(txn.as_ref()).await?;
1346 } else {
1347 E::delete_by_id(pk).exec(&self.db).await?;
1348 }
1349 Ok(Some(model))
1350 }
1351
1352 pub async fn delete_by_id_txn(
1354 &self,
1355 id: i64,
1356 txn: &DatabaseTransaction,
1357 ) -> Result<Option<E::ModelEx>, DbErr>
1358 where
1359 E::PrimaryKey: PrimaryKeyTrait,
1360 <E::PrimaryKey as PrimaryKeyTrait>::ValueType: From<i64>,
1361 E::ModelEx: BaseEntity,
1362 {
1363 let pk = <<E::PrimaryKey as PrimaryKeyTrait>::ValueType as From<i64>>::from(id);
1364 let Some(model) = <E::ModelEx as BaseEntity>::load()
1365 .filter_by_id(id)
1366 .fetch(txn, 0, 1)
1367 .await?
1368 .into_iter()
1369 .next()
1370 else {
1371 return Ok(None);
1372 };
1373 E::delete_by_id(pk).exec(txn).await?;
1374 Ok(Some(model))
1375 }
1376
1377 pub async fn delete_many_txn(
1379 &self,
1380 filter: Option<DeleteQueryData<E>>,
1381 txn: &DatabaseTransaction,
1382 ) -> Result<u64, DbErr> {
1383 self.delete_many_txn_with_opts::<NoUser>(filter, None, txn)
1384 .await
1385 }
1386 pub async fn delete_many_txn_with_opts<U>(
1391 &self,
1392 filter: Option<DeleteQueryData<E>>,
1393 opts: Option<RepositoryOptions<U>>,
1394 txn: &DatabaseTransaction,
1395 ) -> Result<u64, DbErr>
1396 where
1397 U: BaseEntity + Clone + Send + Sync + 'static,
1398 {
1399 let cursor = opts.as_ref().and_then(|o| o.cursor.clone());
1400 let del = filter
1401 .map(|qd| qd.delete)
1402 .unwrap_or_else(|| E::delete_many());
1403 let del = with_cursor_delete(del, &cursor);
1404 if let Some(opts_txn) = opts_txn(opts.as_ref()) {
1405 let res = del.exec(opts_txn.as_ref()).await?;
1408 return Ok(res.rows_affected);
1409 }
1410 let res = del.exec(txn).await?;
1411 Ok(res.rows_affected)
1412 }
1413}
1414
1415pub fn new_uid() -> Uuid {
1418 Uuid::new_v4()
1419}