pub struct GenericCrudRepository<T, D = SoftDelete>{ /* private fields */ }Expand description
Generic PostgreSQL CRUD repository parameterised by entity type T and
delete strategy D (SoftDelete or HardDelete).
All standard CRUD methods are implemented here once. Generated repository
structs wrap this type via newtype + Deref and only add entity-specific
methods (find_by_*, exists_by_*, partial_update, filtered listings).
Implementations§
Source§impl<T, D> GenericCrudRepository<T, D>
impl<T, D> GenericCrudRepository<T, D>
pub fn new(pool: PgPool, table_name: &str) -> Self
pub fn pool(&self) -> &PgPool
pub fn table_name(&self) -> &str
Sourcepub async fn create(&self, entity: &T) -> Result<T>
pub async fn create(&self, entity: &T) -> Result<T>
Insert a new entity row and return the created row.
Sourcepub async fn bulk_create(&self, entities: &[T]) -> Result<Vec<T>>
pub async fn bulk_create(&self, entities: &[T]) -> Result<Vec<T>>
Insert multiple entity rows inside a single transaction.
Sourcepub async fn run_filtered_query(
&self,
pagination: PaginationParams,
base_condition: Option<&str>,
filters: &HashMap<String, String>,
column_types: &HashMap<String, String>,
search_fields: &[&str],
) -> Result<PaginatedResult<T>>
pub async fn run_filtered_query( &self, pagination: PaginationParams, base_condition: Option<&str>, filters: &HashMap<String, String>, column_types: &HashMap<String, String>, search_fields: &[&str], ) -> Result<PaginatedResult<T>>
Execute a filtered / paginated query against this entity’s table.
base_condition — when Some, it is inserted as the __base_condition
filter key which the ORM injects verbatim into the WHERE clause. Use
this to add soft-delete guards without touching the caller-supplied
filters.
NOTE: prefer the *_scoped entry points for anything a client can reach —
they compose the company fence into this condition. This raw form applies no
fence and is for internal/job callers that have already established scope.
Sourcepub async fn run_aggregate_query(
&self,
spec: &AggregateSpec,
base_condition: Option<&str>,
filters: &HashMap<String, String>,
column_types: &HashMap<String, String>,
search_fields: &[&str],
) -> Result<AggregateResult>
pub async fn run_aggregate_query( &self, spec: &AggregateSpec, base_condition: Option<&str>, filters: &HashMap<String, String>, column_types: &HashMap<String, String>, search_fields: &[&str], ) -> Result<AggregateResult>
The aggregate twin of Self::run_filtered_query.
Takes the same base_condition so an aggregate is computed over exactly
the row set the matching list would return — a total that counted
soft-deleted rows, or another tenant’s, would be wrong in a way no
caller could see.
Source§impl<T> GenericCrudRepository<T, SoftDelete>
impl<T> GenericCrudRepository<T, SoftDelete>
Sourcepub async fn find_by_text_field(
&self,
field: &str,
value: &str,
) -> Result<Option<T>>
pub async fn find_by_text_field( &self, field: &str, value: &str, ) -> Result<Option<T>>
Find an active entity by a unique text field.
Filters metadata->>'deleted_at' IS NULL automatically.
Sourcepub async fn exists_by_text_field(
&self,
field: &str,
value: &str,
) -> Result<bool>
pub async fn exists_by_text_field( &self, field: &str, value: &str, ) -> Result<bool>
Check existence by a unique text field (active records only).
Sourcepub async fn find_by_uuid_field(
&self,
field: &str,
value: Uuid,
) -> Result<Option<T>>
pub async fn find_by_uuid_field( &self, field: &str, value: Uuid, ) -> Result<Option<T>>
Find an active entity by a unique UUID field.
Sourcepub async fn exists_by_uuid_field(
&self,
field: &str,
value: Uuid,
) -> Result<bool>
pub async fn exists_by_uuid_field( &self, field: &str, value: Uuid, ) -> Result<bool>
Check existence by a unique UUID field (active records only).
Sourcepub async fn list_paginated_filtered(
&self,
pagination: PaginationParams,
filters: Option<&HashMap<String, String>>,
) -> Result<PaginatedResult<T>>
pub async fn list_paginated_filtered( &self, pagination: PaginationParams, filters: Option<&HashMap<String, String>>, ) -> Result<PaginatedResult<T>>
Paginate active entities with filter and search support.
Requires T: EntityRepoMeta for column type hints and search fields.
Sourcepub async fn aggregate_filtered(
&self,
spec: &AggregateSpec,
filters: Option<&HashMap<String, String>>,
) -> Result<AggregateResult>
pub async fn aggregate_filtered( &self, spec: &AggregateSpec, filters: Option<&HashMap<String, String>>, ) -> Result<AggregateResult>
Group and reduce active entities, skipping the soft-deleted.
Sourcepub async fn list_paginated_filtered_scoped(
&self,
pagination: PaginationParams,
filters: Option<&HashMap<String, String>>,
company: Option<Uuid>,
) -> Result<PaginatedResult<T>>
pub async fn list_paginated_filtered_scoped( &self, pagination: PaginationParams, filters: Option<&HashMap<String, String>>, company: Option<Uuid>, ) -> Result<PaginatedResult<T>>
Paginate active entities, fenced to company.
The company-scoped counterpart of Self::list_paginated_filtered and the one any
client-reachable read should use. Fails closed: a fenced entity with no company
returns MissingCompanyScope rather than every company’s rows.
The fence is ANDed into the same __base_condition the soft-delete guard uses, so
it lands in SQL — which is what keeps total honest. A post-filter in the handler
would fetch limit rows and return the survivors, leaving callers unable to tell
“end of data” from “filtered”, and could not fix COUNT at all.
Sourcepub async fn list_deleted_filtered_scoped(
&self,
pagination: PaginationParams,
filters: Option<&HashMap<String, String>>,
company: Option<Uuid>,
) -> Result<PaginatedResult<T>>
pub async fn list_deleted_filtered_scoped( &self, pagination: PaginationParams, filters: Option<&HashMap<String, String>>, company: Option<Uuid>, ) -> Result<PaginatedResult<T>>
Paginate soft-deleted entities, fenced to company.
/trash is the worse leak of the two: it serves rows whose owners were told the
data is gone. Same fence, same fail-closed contract.
Sourcepub async fn list_deleted_filtered(
&self,
pagination: PaginationParams,
filters: Option<&HashMap<String, String>>,
) -> Result<PaginatedResult<T>>
pub async fn list_deleted_filtered( &self, pagination: PaginationParams, filters: Option<&HashMap<String, String>>, ) -> Result<PaginatedResult<T>>
Paginate soft-deleted entities with filter support.
Sourcepub async fn find_by_id(&self, id: &str) -> Result<Option<T>>
pub async fn find_by_id(&self, id: &str) -> Result<Option<T>>
Find an active (non-deleted) entity by primary key.
Sourcepub async fn update(&self, id: &str, entity: &T) -> Result<Option<T>>
pub async fn update(&self, id: &str, entity: &T) -> Result<Option<T>>
Full update — skips silently if the record is already soft-deleted.
Sourcepub async fn delete(&self, id: &str) -> Result<bool>
pub async fn delete(&self, id: &str) -> Result<bool>
Soft-delete an entity (sets metadata.deleted_at).
Sourcepub async fn exists(&self, id: &str) -> Result<bool>
pub async fn exists(&self, id: &str) -> Result<bool>
Return true if an active entity with the given ID exists.
Sourcepub async fn list_paginated(
&self,
pagination: PaginationParams,
) -> Result<PaginatedResult<T>>
pub async fn list_paginated( &self, pagination: PaginationParams, ) -> Result<PaginatedResult<T>>
Paginate active entities (most-recent-first by ID).
Sourcepub async fn soft_delete(&self, id: &str) -> Result<bool>
pub async fn soft_delete(&self, id: &str) -> Result<bool>
Set metadata.deleted_at to NOW() (soft delete).
Sourcepub async fn restore(&self, id: &str) -> Result<Option<T>>
pub async fn restore(&self, id: &str) -> Result<Option<T>>
Remove deleted_at from metadata, restoring the entity.
Sourcepub async fn list_deleted(
&self,
pagination: PaginationParams,
) -> Result<PaginatedResult<T>>
pub async fn list_deleted( &self, pagination: PaginationParams, ) -> Result<PaginatedResult<T>>
Paginate soft-deleted entities (trash view).
Sourcepub async fn empty_trash(&self) -> Result<u64>
pub async fn empty_trash(&self) -> Result<u64>
Permanently delete all soft-deleted rows (empty trash).
Sourcepub async fn find_deleted_by_id(&self, id: &str) -> Result<Option<T>>
pub async fn find_deleted_by_id(&self, id: &str) -> Result<Option<T>>
Find a soft-deleted entity by primary key.
Sourcepub async fn permanent_delete(&self, id: &str) -> Result<bool>
pub async fn permanent_delete(&self, id: &str) -> Result<bool>
Permanently delete a soft-deleted entity by primary key.
Sourcepub async fn count_active(&self) -> Result<u64>
pub async fn count_active(&self) -> Result<u64>
Count active (non-deleted) entities.
Sourcepub async fn count_deleted(&self) -> Result<u64>
pub async fn count_deleted(&self) -> Result<u64>
Count soft-deleted entities.
Sourcepub async fn bulk_soft_delete(&self, ids: &[String]) -> Result<u64>
pub async fn bulk_soft_delete(&self, ids: &[String]) -> Result<u64>
Soft-delete many active rows atomically.
Sourcepub async fn bulk_restore(&self, ids: &[String]) -> Result<Vec<T>>
pub async fn bulk_restore(&self, ids: &[String]) -> Result<Vec<T>>
Restore many soft-deleted rows atomically, returning the restored rows.
Sourcepub async fn bulk_permanent_delete(&self, ids: &[String]) -> Result<u64>
pub async fn bulk_permanent_delete(&self, ids: &[String]) -> Result<u64>
Permanently delete many soft-deleted rows atomically.
Sourcepub async fn restore_all(&self) -> Result<Vec<T>>
pub async fn restore_all(&self) -> Result<Vec<T>>
Restore every soft-deleted row, returning the restored rows. A single
UPDATE ... RETURNING is atomic, and the returned rows let the service
layer emit a Restored event per entity.
Source§impl<T> GenericCrudRepository<T, HardDelete>
impl<T> GenericCrudRepository<T, HardDelete>
Sourcepub async fn find_by_text_field(
&self,
field: &str,
value: &str,
) -> Result<Option<T>>
pub async fn find_by_text_field( &self, field: &str, value: &str, ) -> Result<Option<T>>
Find an entity by a unique text field (no soft-delete guard).
Sourcepub async fn exists_by_text_field(
&self,
field: &str,
value: &str,
) -> Result<bool>
pub async fn exists_by_text_field( &self, field: &str, value: &str, ) -> Result<bool>
Check existence by a unique text field.
Sourcepub async fn find_by_uuid_field(
&self,
field: &str,
value: Uuid,
) -> Result<Option<T>>
pub async fn find_by_uuid_field( &self, field: &str, value: Uuid, ) -> Result<Option<T>>
Find an entity by a unique UUID field.
Sourcepub async fn exists_by_uuid_field(
&self,
field: &str,
value: Uuid,
) -> Result<bool>
pub async fn exists_by_uuid_field( &self, field: &str, value: Uuid, ) -> Result<bool>
Check existence by a unique UUID field.
Sourcepub async fn list_paginated_filtered(
&self,
pagination: PaginationParams,
filters: Option<&HashMap<String, String>>,
) -> Result<PaginatedResult<T>>
pub async fn list_paginated_filtered( &self, pagination: PaginationParams, filters: Option<&HashMap<String, String>>, ) -> Result<PaginatedResult<T>>
Paginate entities with filter and search support.
Sourcepub async fn aggregate_filtered(
&self,
spec: &AggregateSpec,
filters: Option<&HashMap<String, String>>,
) -> Result<AggregateResult>
pub async fn aggregate_filtered( &self, spec: &AggregateSpec, filters: Option<&HashMap<String, String>>, ) -> Result<AggregateResult>
Group and reduce entities. This mode has no trash to exclude.
Sourcepub async fn find_by_id(&self, id: &str) -> Result<Option<T>>
pub async fn find_by_id(&self, id: &str) -> Result<Option<T>>
Find an entity by primary key.
Sourcepub async fn delete(&self, id: &str) -> Result<bool>
pub async fn delete(&self, id: &str) -> Result<bool>
Permanently delete an entity by primary key.
Sourcepub async fn exists(&self, id: &str) -> Result<bool>
pub async fn exists(&self, id: &str) -> Result<bool>
Return true if an entity with the given ID exists.
Sourcepub async fn list_paginated(
&self,
pagination: PaginationParams,
) -> Result<PaginatedResult<T>>
pub async fn list_paginated( &self, pagination: PaginationParams, ) -> Result<PaginatedResult<T>>
Paginate entities (most-recent-first by ID).
Sourcepub async fn bulk_delete(&self, ids: &[String]) -> Result<u64>
pub async fn bulk_delete(&self, ids: &[String]) -> Result<u64>
Hard-delete many rows atomically. Rolls back unless every id matched.
Sourcepub async fn bulk_update(&self, entities: &[T]) -> Result<Vec<T>>
pub async fn bulk_update(&self, entities: &[T]) -> Result<Vec<T>>
Update many rows atomically. No soft-delete guard in this mode.
Auto Trait Implementations§
impl<T, D = SoftDelete> !RefUnwindSafe for GenericCrudRepository<T, D>
impl<T, D = SoftDelete> !UnwindSafe for GenericCrudRepository<T, D>
impl<T, D> Freeze for GenericCrudRepository<T, D>
impl<T, D> Send for GenericCrudRepository<T, D>
impl<T, D> Sync for GenericCrudRepository<T, D>
impl<T, D> Unpin for GenericCrudRepository<T, D>
impl<T, D> UnsafeUnpin for GenericCrudRepository<T, D>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more