Skip to main content

GenericCrudService

Struct GenericCrudService 

Source
pub struct GenericCrudService<E, C, U, R>{ /* private fields */ }
Expand description

A fully generic CRUD service.

Type parameters:

  • E — entity type (must implement PersistentEntity)
  • C — create DTO (entity implements FromCreateDto<C>)
  • U — update DTO (entity implements ApplyUpdateDto<U>)
  • R — repository (implements CrudRepository<E>)

The service always holds an Arc<dyn CrudEventPublisher<E>>. When no publisher is needed, NoOpCrudEventPublisher is used (zero overhead). Event-publish errors are fire-and-forget — they never fail the operation.

Implementations§

Source§

impl<E, C, U, R> GenericCrudService<E, C, U, R>
where E: PersistentEntity + Clone + FromCreateDto<C> + ApplyUpdateDto<U>, C: Send + Sync + 'static, U: Send + Sync + 'static, R: CrudRepository<E>,

Source

pub fn new( repository: Arc<R>, lifecycle: Arc<dyn ServiceLifecycle<E>>, event_publisher: Arc<dyn CrudEventPublisher<E>>, ) -> Self

Full constructor — supply all three dependencies.

Source

pub fn with_lifecycle( repository: Arc<R>, lifecycle: Arc<dyn ServiceLifecycle<E>>, ) -> Self

Create with a custom lifecycle and the no-op event publisher.

Source

pub fn with_repository(repository: Arc<R>) -> Self

Create with the no-op lifecycle and no-op event publisher (generated default).

Source

pub fn with_event_publisher( self, publisher: Arc<dyn CrudEventPublisher<E>>, ) -> Self

Replace the event publisher on an existing service (builder pattern).

Source

pub fn repository(&self) -> &R

Access the underlying repository for custom query extensions.

Source

pub async fn list( &self, page: u32, limit: u32, filters: HashMap<String, String>, ) -> ServiceResult<(Vec<E>, u64)>

Source

pub async fn list_with_info( &self, page: u32, limit: u32, filters: HashMap<String, String>, ) -> ServiceResult<(Vec<E>, PaginationInfo)>

list, carrying the pagination info (cursor positions included) so the HTTP layer can surface keyset paging on every generic route.

Source

pub async fn aggregate( &self, spec: &AggregateSpec, filters: HashMap<String, String>, ) -> ServiceResult<AggregateResult>

Group and reduce the rows list would return, under the same filters.

Source

pub async fn create(&self, dto: C) -> ServiceResult<E>

Source

pub async fn get_by_id(&self, id: &str) -> ServiceResult<Option<E>>

Source

pub async fn update(&self, id: &str, dto: U) -> ServiceResult<Option<E>>

Source

pub async fn soft_delete(&self, id: &str) -> ServiceResult<bool>

Source

pub async fn restore(&self, id: &str) -> ServiceResult<Option<E>>

Source

pub async fn hard_delete(&self, id: &str) -> ServiceResult<bool>

Source

pub async fn list_deleted( &self, page: u32, limit: u32, ) -> ServiceResult<(Vec<E>, u64)>

Source

pub async fn empty_trash(&self) -> ServiceResult<u64>

Source

pub async fn count(&self) -> ServiceResult<u64>

Source

pub async fn count_active(&self) -> ServiceResult<u64>

Source

pub async fn find_by_id(&self, id: &str) -> ServiceResult<Option<E>>

Alias for get_by_id — used by generated handlers.

Source

pub async fn permanent_delete(&self, id: &str) -> ServiceResult<bool>

Alias for hard_delete — used by generated handlers.

Source

pub async fn count_deleted(&self) -> ServiceResult<u64>

Count soft-deleted entities.

Source

pub async fn get_deleted_by_id(&self, id: &str) -> ServiceResult<Option<E>>

Retrieve a soft-deleted entity by ID (includes deleted records).

Source

pub async fn list_deleted_filtered( &self, page: u32, limit: u32, _filters: HashMap<String, String>, ) -> ServiceResult<(Vec<E>, u64)>

List deleted entities with filter support (filters currently ignored, delegates to list_deleted).

Source

pub async fn upsert(&self, dto: C) -> ServiceResult<E>

Upsert: create the entity if it doesn’t exist (no ID-based lookup — always creates).

Source

pub async fn partial_update( &self, id: &str, fields: HashMap<String, Value>, ) -> ServiceResult<Option<E>>

Apply a partial update from a JSON map of field values.

Fields not present in the map are left unchanged.

Source

pub async fn bulk_create(&self, dtos: Vec<C>) -> ServiceResult<Vec<E>>
where E: FromCreateDto<C>,

Source

pub async fn bulk_soft_delete(&self, ids: Vec<String>) -> ServiceResult<u64>

Soft-delete many entities by id (atomic).

Source

pub async fn bulk_restore(&self, ids: Vec<String>) -> ServiceResult<Vec<E>>

Restore many soft-deleted entities by id (atomic).

Source

pub async fn restore_all(&self) -> ServiceResult<u64>

Restore every soft-deleted entity (atomic). Returns the number restored. Emits a Restored event per entity, mirroring bulk_restore.

Source

pub async fn bulk_permanent_delete( &self, ids: Vec<String>, ) -> ServiceResult<u64>

Permanently delete many soft-deleted entities by id (atomic).

Source

pub async fn bulk_update( &self, items: Vec<(String, U)>, ) -> ServiceResult<Vec<E>>

Full-update many entities (atomic). Each item is (id, UpdateDto).

Source

pub async fn bulk_partial_update( &self, items: Vec<(String, HashMap<String, Value>)>, ) -> ServiceResult<Vec<E>>

Partial-update many entities (atomic). Each item is (id, field_map).

Trait Implementations§

Source§

impl<E, C, U, R> Clone for GenericCrudService<E, C, U, R>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<E, C, U, R> CrudService<E, C, U> for GenericCrudService<E, C, U, R>
where E: PersistentEntity + Clone + Serialize + DeserializeOwned + FromCreateDto<C> + ApplyUpdateDto<U> + Send + Sync + 'static, C: Send + Sync + 'static, U: Send + Sync + 'static, R: CrudRepository<E> + Send + Sync + 'static,

Blanket impl so that any GenericCrudService<E, C, U, R> automatically satisfies CrudService<E, C, U> without generated adapter structs.

Generated code emits only a type alias: pub type UserService = GenericCrudService<User, CreateUserDto, UpdateUserDto, UserRepository>;

That alias now directly implements CrudService and can be passed to BackboneCrudHandler without any additional boilerplate.

Source§

type Error = ServiceError

Error type for service operations
Source§

fn entity_name() -> &'static str

Entity name for error messages (e.g., “User”, “Role”)
Hydrate ?include= relations: fetch rows from table by id list, as JSON. Default: no expansion. GenericCrudService delegates to its repository.
Source§

fn list<'life0, 'async_trait>( &'life0 self, page: u32, limit: u32, filters: HashMap<String, String>, ) -> Pin<Box<dyn Future<Output = Result<(Vec<E>, u64), ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

List entities with pagination and filters Read more
Source§

fn list_with_info<'life0, 'async_trait>( &'life0 self, page: u32, limit: u32, filters: HashMap<String, String>, ) -> Pin<Box<dyn Future<Output = Result<(Vec<E>, PaginationInfo), ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

list, carrying the pagination info (cursor positions included) so the HTTP layer can surface keyset paging. Default: the tuple form — generated services override this with the repository’s cursors.
Source§

fn aggregate<'life0, 'life1, 'async_trait>( &'life0 self, spec: &'life1 AggregateSpec, filters: HashMap<String, String>, ) -> Pin<Box<dyn Future<Output = Result<AggregateResult, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Group and reduce the rows list would return, under the same filters. Read more
Source§

fn table_name(&self) -> Option<&str>

The schema-qualified table this service reads, when it has one. Read more
Source§

fn create<'life0, 'async_trait>( &'life0 self, dto: C, ) -> Pin<Box<dyn Future<Output = Result<E, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Create a new entity Read more
Source§

fn get_by_id<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<Option<E>, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Get entity by ID Read more
Source§

fn update<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, dto: U, ) -> Pin<Box<dyn Future<Output = Result<Option<E>, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Full update of entity Read more
Source§

fn partial_update<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, fields: HashMap<String, Value>, ) -> Pin<Box<dyn Future<Output = Result<Option<E>, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Partial update with specific fields Read more
Source§

fn soft_delete<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<bool, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Soft delete entity Read more
Source§

fn bulk_create<'life0, 'async_trait>( &'life0 self, items: Vec<C>, ) -> Pin<Box<dyn Future<Output = Result<Vec<E>, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Bulk create multiple entities Read more
Source§

fn upsert<'life0, 'async_trait>( &'life0 self, dto: C, ) -> Pin<Box<dyn Future<Output = Result<E, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Upsert (create or update) Read more
Source§

fn list_deleted<'life0, 'async_trait>( &'life0 self, page: u32, limit: u32, ) -> Pin<Box<dyn Future<Output = Result<(Vec<E>, u64), ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

List deleted entities (trash) Read more
Source§

fn restore<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<Option<E>, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Restore soft-deleted entity Read more
Source§

fn empty_trash<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<u64, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Permanently delete all soft-deleted entities Read more
Source§

fn get_deleted_by_id<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<Option<E>, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Get deleted entity by ID (from trash) Read more
Source§

fn permanent_delete<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<bool, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Permanently delete a single soft-deleted entity by ID Read more
Source§

fn list_deleted_filtered<'life0, 'async_trait>( &'life0 self, page: u32, limit: u32, filters: HashMap<String, String>, ) -> Pin<Box<dyn Future<Output = Result<(Vec<E>, u64), ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

List deleted entities with filters Read more
Source§

fn count_active<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<u64, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Count active (non-deleted) entities Read more
Source§

fn count_deleted<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<u64, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Count deleted entities in trash Read more
Source§

fn bulk_soft_delete<'life0, 'async_trait>( &'life0 self, ids: Vec<String>, ) -> Pin<Box<dyn Future<Output = Result<u64, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Soft-delete many entities by id. Returns the number affected. Read more
Source§

fn bulk_restore<'life0, 'async_trait>( &'life0 self, ids: Vec<String>, ) -> Pin<Box<dyn Future<Output = Result<Vec<E>, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Restore many soft-deleted entities by id. Returns the restored rows. Read more
Source§

fn bulk_permanent_delete<'life0, 'async_trait>( &'life0 self, ids: Vec<String>, ) -> Pin<Box<dyn Future<Output = Result<u64, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Permanently delete many soft-deleted entities by id. Read more
Source§

fn restore_all<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<u64, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Restore every soft-deleted entity. Returns the number restored. Read more
Source§

fn bulk_update<'life0, 'async_trait>( &'life0 self, items: Vec<(String, U)>, ) -> Pin<Box<dyn Future<Output = Result<Vec<E>, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Full-update many entities. Each item is (id, UpdateDto). Read more
Source§

fn bulk_partial_update<'life0, 'async_trait>( &'life0 self, items: Vec<(String, HashMap<String, Value>)>, ) -> Pin<Box<dyn Future<Output = Result<Vec<E>, ServiceError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Partial-update many entities. Each item is (id, field_map). Read more
Source§

fn count_active_filtered<'life0, 'async_trait>( &'life0 self, filters: HashMap<String, String>, ) -> Pin<Box<dyn Future<Output = Result<u64, Self::Error>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Count active entities MATCHING the caller’s filters. Read more

Auto Trait Implementations§

§

impl<E, C, U, R> !RefUnwindSafe for GenericCrudService<E, C, U, R>

§

impl<E, C, U, R> !UnwindSafe for GenericCrudService<E, C, U, R>

§

impl<E, C, U, R> Freeze for GenericCrudService<E, C, U, R>

§

impl<E, C, U, R> Send for GenericCrudService<E, C, U, R>

§

impl<E, C, U, R> Sync for GenericCrudService<E, C, U, R>

§

impl<E, C, U, R> Unpin for GenericCrudService<E, C, U, R>

§

impl<E, C, U, R> UnsafeUnpin for GenericCrudService<E, C, U, R>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more