oscorped
oscorped

Reputation: 1

RUST DIESEL generic repository

Im trying to implement a generic repository for any model i have. The problem im facing now as i understand lies in trait bounds for struct which are derived with Queryable (DBSchema), Insertable (InsertDBSchema) and AsChangeset (UpdateDBSchema)

Here is the code of traits

pub trait DieselCrudMeta: Send + Sync {
    type Schema: Send;
    type CreateSchema: Send;
    type UpdateSchema: Send;
    type Filter: Send;
    type Error: Send;

    type DBSchema: Send + Queryable<Self::SqlType, Pg> + From<Self::Schema> + Into<Self::Schema>;
    type InsertDBSchema: Insertable<Self::Table> + From<Self::CreateSchema> + Send;
    type UpdateDBSchema: From<Self::UpdateSchema> + Send;
    // type Table: diesel::Table + BoxedDsl<'static, Pg, Output=BoxedSelectStatement<'static, Self::SqlType, FromClause<Self::Table>, Pg>>;
    type Table: diesel::Table;
    type SqlType: SqlTypeOrSelectable + Send;

    fn table() -> Self::Table;
    // fn to_db_insert_schema(schema: Self::CreateSchema) -> Self::InsertDBSchema;
    // fn apply_filters(
    //     query: BoxedSelectStatement<'static, Self::SqlType, FromClause<Self::Table>, Pg>,
    //     filters: Vec<Self::Filter>
    // ) -> BoxedSelectStatement<'static, Self::SqlType, FromClause<Self::Table>, Pg>;
}

#[async_trait]
pub trait Repository: Send + Sync {
    type Schema;
    type CreateSchema;
    type UpdateSchema;
    type Error;
    type Filter;

    async fn create(&self, schema: Self::CreateSchema) -> Result<Self::Schema, Self::Error>;
    async fn get(&self, id: i32) -> Result<Self::Schema, Self::Error>;
    async fn get_one(&self, filters: Vec<Self::Filter>) -> Result<Self::Schema, Self::Error>;
    async fn get_many(&self, filters: Vec<Self::Filter>, page: Option<u32>, limit: Option<u32>) -> Result<Vec<Self::Schema>, Self::Error>;
    async fn count(&self, filters: Vec<Self::Filter>) -> Result<i64, Self::Error>;
    async fn update(
        &self,
        id: i32,
        schema: Self::UpdateSchema,
    ) -> Result<Self::Schema, Self::Error>;
    async fn delete(&self, id: i32) -> Result<(), Self::Error>;
}

here is the code of my attempt to implement it

#[async_trait]
impl<T: DieselCrudMeta> Repository for DieselRepository<T> {
    type Schema = T::Schema;
    type CreateSchema = T::CreateSchema;
    type UpdateSchema = T::UpdateSchema;
    type Error = T::Error;
    type Filter = T::Filter;

    async fn create(&self, schema: Self::CreateSchema) -> Result<Self::Schema, Self::Error> {
        let insert = T::InsertDBSchema::from(schema);
        // let insert = T::to_db_insert_schema(schema);
        let pool = self.pool.clone();

        let result = run(move || {
            let mut conn = pool.get().unwrap();
            // insert
            diesel::insert_into(T::table())
                .values(insert)
                .get_result::<T::DBSchema>(&mut conn)
        })
            .await
            .unwrap();

        Ok(result.into())
    }

    async fn get(&self, id: i32) -> Result<Self::Schema, Self::Error> {
        todo!()
    }

    async fn get_one(&self, filters: Vec<Self::Filter>) -> Result<Self::Schema, Self::Error> {
        todo!()
    }

    async fn get_many(&self, filters: Vec<Self::Filter>, page: Option<u32>, limit: Option<u32>) -> Result<Vec<Self::Schema>, Self::Error> {
        todo!()
    }

    async fn count(&self, filters: Vec<Self::Filter>) -> Result<i64, Self::Error> {
        todo!()
    }

    async fn update(&self, id: i32, schema: Self::UpdateSchema) -> Result<Self::Schema, Self::Error> {
        todo!()
    }

    async fn delete(&self, id: i32) -> Result<(), Self::Error> {
        todo!()
    }
}

First problem i encounetered wath tied to trait evaluating overflow, but it was solved by removing & before insert variable in .values() call. But i dont fully understand why it happened and will appreciate solutions.

But now even with first problem "fixed" whatever i try (i tried a lot of combiantions of bounds etc.) I encounter one final problem which looks like that

error[E0277]: the trait bound `InsertStatement<<T as DieselCrudMeta>::Table, <<T as DieselCrudMeta>::InsertDBSchema as diesel::Insertable<<T as DieselCrudMeta>::Table>>::Values>: LoadQuery<'_, _, <T as DieselCrudMeta>::DBSchema>` is not satisfied
    --> src/infrastructure/database/repositories/mod.rs:97:44
     |
97   |                 .get_result::<T::DBSchema>(&mut conn)
     |                  ----------                ^^^^^^^^^ the trait `AsQuery` is not implemented for `InsertStatement<<T as DieselCrudMeta>::Table, <<T as DieselCrudMeta>::InsertDBSchema as diesel::Insertable<<T as DieselCrudMeta>::Table>>::Values>`
     |                  |
     |                  required by a bound introduced by this call
     |
     = note: required for `InsertStatement<<T as DieselCrudMeta>::Table, <<T as DieselCrudMeta>::InsertDBSchema as diesel::Insertable<<T as DieselCrudMeta>::Table>>::Values>` to implement `LoadQuery<'_, _, <T as DieselCrudMeta>::DBSchema>`
note: required by a bound in `get_result`
    --> /Users/tony/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/diesel-2.2.7/src/query_dsl/mod.rs:1722:15
     |
1720 |     fn get_result<'query, U>(self, conn: &mut Conn) -> QueryResult<U>
     |        ---------- required by a bound in this associated function
1721 |     where
1722 |         Self: LoadQuery<'query, Conn, U>,
     |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::get_result`
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
     |
80   | impl<T: DieselCrudMeta> Repository for DieselRepository<T> where InsertStatement<<T as DieselCrudMeta>::Table, <<T as DieselCrudMeta>::InsertDBSchema as diesel::Insertable<<T as DieselCrudMeta>::Table>>::Values>: AsQuery {
     |                                                            +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

which when attempted to fix like compiler suggests transforms into next one

error[E0277]: the trait bound `Pg: QueryMetadata<<InsertStatement<<T as DieselCrudMeta>::Table, <<T as DieselCrudMeta>::InsertDBSchema as diesel::Insertable<<T as DieselCrudMeta>::Table>>::Values> as AsQuery>::SqlType>` is not satisfied
    --> src/infrastructure/database/repositories/mod.rs:97:44
     |
97   |                 .get_result::<T::DBSchema>(&mut conn)
     |                  ----------                ^^^^^^^^^ the trait `HasSqlType<<InsertStatement<<T as DieselCrudMeta>::Table, <<T as DieselCrudMeta>::InsertDBSchema as diesel::Insertable<<T as DieselCrudMeta>::Table>>::Values> as AsQuery>::SqlType>` is not implemented for `Pg`
     |                  |
     |                  required by a bound introduced by this call
     |
     = help: the following other types implement trait `HasSqlType<ST>`:
               `Pg` implements `HasSqlType<BigInt>`
               `Pg` implements `HasSqlType<CChar>`
               `Pg` implements `HasSqlType<Cidr>`
               `Pg` implements `HasSqlType<Citext>`
               `Pg` implements `HasSqlType<Inet>`
               `Pg` implements `HasSqlType<MacAddr>`
               `Pg` implements `HasSqlType<Money>`
               `Pg` implements `HasSqlType<Oid>`
             and 24 others
     = note: required for `Pg` to implement `QueryMetadata<<InsertStatement<<T as DieselCrudMeta>::Table, <<T as DieselCrudMeta>::InsertDBSchema as diesel::Insertable<<T as DieselCrudMeta>::Table>>::Values> as AsQuery>::SqlType>`
     = note: required for `InsertStatement<<T as DieselCrudMeta>::Table, <<T as DieselCrudMeta>::InsertDBSchema as diesel::Insertable<<T as DieselCrudMeta>::Table>>::Values>` to implement `LoadQuery<'_, PooledConnection<ConnectionManager<PgConnection>>, <T as DieselCrudMeta>::DBSchema>`
note: required by a bound in `get_result`
    --> /Users/tony/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/diesel-2.2.7/src/query_dsl/mod.rs:1722:15
     |
1720 |     fn get_result<'query, U>(self, conn: &mut Conn) -> QueryResult<U>
     |        ---------- required by a bound in this associated function
1721 |     where
1722 |         Self: LoadQuery<'query, Conn, U>,
     |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `RunQueryDsl::get_result`

maybe someone tried to implement something like this on trait level

Already seen this answer https://stackoverflow.com/a/79469407

Any help will be appreciated!

Generic repository in rust with diesel

Upvotes: -1

Views: 37

Answers (0)

Related Questions