use crate::{ db::{add_listener, remove_listener}, error::MyError, label::ArbiterLabel, }; use activitystreams::primitives::XsdAnyUri; use actix::prelude::*; use bb8_postgres::{bb8, tokio_postgres, PostgresConnectionManager}; use log::{error, info}; use tokio::sync::oneshot::{channel, Receiver}; #[derive(Clone)] pub struct Db { actor: Addr, } pub type Pool = bb8::Pool>; pub enum DbActorState { Waiting(tokio_postgres::Config), Ready(Pool), } pub struct DbActor { pool: DbActorState, } pub struct DbQuery(pub F); impl Db { pub fn new(config: tokio_postgres::Config) -> Db { let actor = Supervisor::start(|_| DbActor { pool: DbActorState::new_empty(config), }); Db { actor } } pub async fn execute_inline(&self, f: F) -> Result where T: Send + 'static, F: FnOnce(Pool) -> Fut + Send + 'static, Fut: Future, { Ok(self.actor.send(DbQuery(f)).await?.await?) } pub async fn remove_listener(&self, inbox: XsdAnyUri) -> Result<(), MyError> { self.execute_inline(move |pool: Pool| { let inbox = inbox.clone(); async move { let conn = pool.get().await?; remove_listener(&conn, &inbox).await } }) .await? .map_err(MyError::from) } pub async fn add_listener(&self, inbox: XsdAnyUri) -> Result<(), MyError> { self.execute_inline(move |pool: Pool| { let inbox = inbox.clone(); async move { let conn = pool.get().await?; add_listener(&conn, &inbox).await } }) .await? .map_err(MyError::from) } } impl DbActorState { pub fn new_empty(config: tokio_postgres::Config) -> Self { DbActorState::Waiting(config) } pub async fn new(config: tokio_postgres::Config) -> Result { let manager = PostgresConnectionManager::new(config, tokio_postgres::tls::NoTls); let pool = bb8::Pool::builder().max_size(8).build(manager).await?; Ok(DbActorState::Ready(pool)) } } impl Actor for DbActor { type Context = Context; fn started(&mut self, ctx: &mut Self::Context) { info!("Starting DB Actor in {}", ArbiterLabel::get()); match self.pool { DbActorState::Waiting(ref config) => { let fut = DbActorState::new(config.clone()) .into_actor(self) .map(|res, actor, ctx| { match res { Ok(pool) => { info!("DB pool created in {}", ArbiterLabel::get()); actor.pool = pool; } Err(e) => { error!( "Error starting DB Actor in {}, {}", ArbiterLabel::get(), e ); ctx.stop(); } }; }); ctx.wait(fut); } _ => (), }; } } impl Supervised for DbActor {} impl Handler> for DbActor where F: FnOnce(Pool) -> Fut + 'static, Fut: Future, R: Send + 'static, { type Result = ResponseFuture>; fn handle(&mut self, msg: DbQuery, ctx: &mut Self::Context) -> Self::Result { let (tx, rx) = channel(); let pool = match self.pool { DbActorState::Ready(ref pool) => pool.clone(), _ => { error!("Tried to query DB before ready"); return Box::pin(async move { rx }); } }; ctx.spawn( async move { let result = (msg.0)(pool).await; let _ = tx.send(result); } .into_actor(self), ); Box::pin(async move { rx }) } } impl Message for DbQuery where F: FnOnce(Pool) -> Fut, Fut: Future, R: Send + 'static, { type Result = Receiver; }