I'm using Actix and Diesel. The way I set it up is that I have a DbExecutor - an async worker which takes messages and returns the results of the queries they represent, like so:
#[derive(Message)]
#[rtype(result = "Result<User>")]
pub struct RetrieveUser {
pub uid: i64,
}
impl Handler<RetrieveUser> for DbExecutor {
// ...
fn handle(&mut self, msg: InsertFile, _: &mut Self::Context) -> Self::Result {
use crate::schema::users::dsl::*;
// ...
Ok(users.filter(uid.eq(msg.uid)).execute(connection))
}
}
fn my_route_handler() {
db_executor.send(RetrieveUser{ uid: 123 });
//...
}
However, there is a table which doesn't fit neatly into these simple queries, and I would like to specify the filters from the side of the message sender. I was thinking I could send a message containing a closure which takes the users object, and then execute it on the side of the DbExecutor, returning the results:
fn my_ideal_route_handler(){
db_executor.send(RetrieveUsers { predicate: |users| users.filter(uid.eq(123)) });
}
However, I am stuck, seeing as all Messages have to implement Copy, and the Diesel types are confusing - I am not sure how to specify the type I need generically enough, while getting it to compile, eg.:
// FAILS
#[derive(Message)]
#[rtype(result = "Result<Vec<User>>")]
pub struct RetrieveUsers {
pub predicate: dyn FnOnce(dyn QueryDsl) -> dyn RunQueryDsl<PgConnection>,
}
I am also not sure whether this approach is even sensible at all, so I am open to other suggestions which would:
- allow me to query the table dynamically
- still keep most of the database handling separate and not all across the project as if I were passing the plain database connection around
- (ideally) utilize concurrent access to the database