actix_diesel_actor/
lib.rs1use ::actix::prelude::*;
2use diesel::pg::Pg;
3use diesel::prelude::*;
4use diesel::query_builder::{QueryFragment, QueryId, SelectQuery};
5use diesel::query_dsl::{LoadQuery, RunQueryDsl};
6use diesel::r2d2;
7use diesel::r2d2::{ConnectionManager, Pool};
8use diesel::result::Error;
9use dotenv::dotenv;
10use std::env;
11use std::marker::PhantomData;
12
13use log::debug;
14
15pub struct DbExecutor<T: 'static>
16where
17 T: Connection,
18{
19 pub pool: Pool<ConnectionManager<T>>,
20}
21impl<T: Connection> Actor for DbExecutor<T> {
22 type Context = SyncContext<Self>;
23
24 fn started(&mut self, _ctx: &mut Self::Context) {
25 debug!("I am alive!");
26 }
27}
28
29pub struct SQuery<S, I> {
37 pub select: S,
38 pub phantom: PhantomData<I>,
39}
40impl<S, I: 'static> Message for SQuery<S, I> {
41 type Result = Result<Vec<I>, Error>;
42}
43impl<S: LoadQuery<PgConnection, I> + SelectQuery + QueryFragment<Pg>, I: 'static>
44 Handler<SQuery<S, I>> for DbExecutor<PgConnection>
45{
46 type Result = Result<Vec<I>, Error>;
47
48 fn handle(&mut self, msg: SQuery<S, I>, _: &mut Self::Context) -> Self::Result {
49 let dbg = diesel::debug_query(&msg.select);
50 debug!("{:?}", dbg);
51 let pool = &self.pool;
52 if let Ok(conn) = pool.get() {
53 let res = msg.select.load::<I>(&conn);
54 return res;
55 }
56 Ok(Vec::new())
57 }
58}
59
60pub struct WQuery<W, I> {
74 pub query: W,
75 pub phantom: PhantomData<I>,
76}
77impl<W, I: 'static> Message for WQuery<W, I> {
78 type Result = Result<Vec<I>, Error>;
79}
80impl<W: LoadQuery<PgConnection, I> + QueryFragment<Pg>, I: 'static> Handler<WQuery<W, I>>
81 for DbExecutor<PgConnection>
82{
83 type Result = Result<Vec<I>, Error>;
84
85 fn handle(&mut self, msg: WQuery<W, I>, _: &mut Self::Context) -> Self::Result {
86 let dbg = diesel::debug_query(&msg.query);
87 debug!("{:?}", dbg);
88 let pool = &self.pool;
89 if let Ok(conn) = pool.get() {
90 let res = msg.query.get_results::<I>(&conn);
91 return res;
92 }
93 Ok(Vec::new())
94 }
95}
96
97pub struct AppState {
98 pub rdb: Addr<DbExecutor<diesel::PgConnection>>,
99 pub wdb: Addr<DbExecutor<diesel::PgConnection>>,
100}
101
102pub enum ConnectionType {
108 Read,
109 Write,
110}
111pub fn db_setup(conn_type: ConnectionType) -> actix::Addr<DbExecutor<diesel::PgConnection>> {
112 dotenv().ok();
113
114 let var = match conn_type {
115 ConnectionType::Read => "DB_READ_URL",
116 ConnectionType::Write => "DB_WRITE_URL",
117 };
118 let database_url = env::var(var).unwrap_or_else(|_| panic!("{} must be set", var));
119 let manager = ConnectionManager::<PgConnection>::new(database_url);
120 let pool = r2d2::Pool::builder()
121 .max_size(3)
122 .min_idle(Some(0))
123 .build(manager)
124 .expect("Failed to create pool.");
125 SyncArbiter::start(3, move || DbExecutor { pool: pool.clone() })
126}
127
128#[derive(Debug)]
129pub enum DbExecutorError {
130 DatabaseError(diesel::result::Error),
131 MailBoxError(actix::MailboxError),
132 Unknown,
133}
134impl From<diesel::result::Error> for DbExecutorError {
135 fn from(error: diesel::result::Error) -> Self {
136 DbExecutorError::DatabaseError(error)
137 }
138}
139impl From<actix::MailboxError> for DbExecutorError {
140 fn from(error: actix::MailboxError) -> Self {
141 DbExecutorError::MailBoxError(error)
142 }
143}
144
145pub struct Conn;
150impl Message for Conn {
151 type Result = Result<r2d2::PooledConnection<ConnectionManager<PgConnection>>, Error>;
152}
153impl Handler<Conn> for DbExecutor<PgConnection> {
154 type Result = Result<r2d2::PooledConnection<ConnectionManager<PgConnection>>, Error>;
155
156 fn handle(&mut self, _msg: Conn, _: &mut Self::Context) -> Self::Result {
157 let pool = &self.pool;
159 if let Ok(conn) = pool.get() {
160 return Ok(conn);
161 }
162 Err(Error::NotFound)
163 }
164}
165
166pub struct DQuery<D> {
173 pub query: D,
174}
175impl<D> Message for DQuery<D> {
176 type Result = Result<usize, Error>;
177}
178impl<D: RunQueryDsl<PgConnection> + QueryId + QueryFragment<Pg>> Handler<DQuery<D>>
179 for DbExecutor<PgConnection>
180{
181 type Result = Result<usize, Error>;
182
183 fn handle(&mut self, msg: DQuery<D>, _: &mut Self::Context) -> Self::Result {
184 let dbg = diesel::debug_query(&msg.query);
185 debug!("{:?}", dbg);
186 let pool = &self.pool;
187 if let Ok(conn) = pool.get() {
188 let res = msg.query.execute(&conn);
189 return res;
190 }
191 Err(Error::NotFound)
192 }
193}