pub trait EsRepo: Send {
type Entity: EsEntity;
type CreateError;
type ModifyError;
type FindError: From<Error> + From<EntityHydrationError> + Send;
type QueryError: From<Error> + From<EntityHydrationError> + Send;
type EsQueryFlavor;
// Required methods
fn nested_tree_spec() -> TreeSpec;
fn hydrate_nested_from_rows<E>(
rows_by_tag: &mut HashMap<i32, Vec<Row>>,
tag_cursor: &mut i32,
entities: &mut [Self::Entity],
) -> Result<(), E>
where E: From<Error> + From<EntityHydrationError>;
}Expand description
Required trait for all repositories to be compatible with es-entity and generate functions.
All repositories implement this trait to satisfy the basic requirements for
type-safe database operations with the associated entity. The trait ensures validation
that required fields (like entity) are present with compile-time errors.
Implemented by the EsRepo derive macro with #[es_repo] attributes.
§Example
// Would show error for missing entity field if not provided in the `es_repo` attribute
#[derive(EsRepo, Debug)]
#[es_repo(entity = "User", columns(name(ty = "String")))]
pub struct Users {
pool: PgPool, // Required field for database operations
}
impl Users {
pub fn new(pool: PgPool) -> Self {
Self { pool }
}
}§in_op_only: making the operation mandatory
#[es_repo(in_op_only)] generates only the _in_op variants of every
repo fn. The standalone fns are exactly the ones that open (or borrow) the
pool on the caller’s behalf, so removing them makes passing an operation —
an AtomicOperation for writes, an
IntoOneTimeExecutor for reads — the only way
to reach the database.
With no standalone fn left to open one, the pool field becomes optional. A repo that holds no pool cannot begin its own operation, which is the point: the discipline is enforced by construction rather than by convention.
Calling a non-_in_op fn on such a repo does not compile — the method does
not exist (note the two tests below compile a real repo, so they need the
test database, like the book’s examples):
use es_entity::*;
use serde::{Deserialize, Serialize};
// This repo deliberately KEEPS its pool, so that `create`, were it ever
// generated again, would compile: that makes the E0599 below prove the fn
// is absent, rather than merely that its body could not build an operation.
#[derive(EsRepo)]
#[es_repo(entity = "User", in_op_only, columns(name(ty = "String")))]
pub struct Users {
pool: es_entity::db::Pool,
}
async fn reaches_the_db_without_an_op(repo: &Users, new_user: NewUser) {
// error[E0599]: no method named `create` found — `in_op_only` leaves
// only `create_in_op`, which demands an operation from the caller.
repo.create(new_user).await.unwrap();
}The _in_op twin of the very same call compiles:
use es_entity::*;
use serde::{Deserialize, Serialize};
#[derive(EsRepo)]
#[es_repo(entity = "User", in_op_only, columns(name(ty = "String")))]
pub struct Users {}
async fn takes_the_op_from_its_caller(
repo: &Users,
op: &mut impl AtomicOperation,
new_user: NewUser,
) {
repo.create_in_op(op, new_user).await.unwrap();
}Required Associated Types§
type Entity: EsEntity
type CreateError
type ModifyError
type FindError: From<Error> + From<EntityHydrationError> + Send
type QueryError: From<Error> + From<EntityHydrationError> + Send
type EsQueryFlavor
Required Methods§
fn nested_tree_spec() -> TreeSpec
fn hydrate_nested_from_rows<E>( rows_by_tag: &mut HashMap<i32, Vec<Row>>, tag_cursor: &mut i32, entities: &mut [Self::Entity], ) -> Result<(), E>
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".