use std::sync::Arc;
use crate::data::DataError;
#[cfg(feature = "db-diesel-sqlite")]
pub type DieselConn = diesel::sqlite::SqliteConnection;
#[cfg(all(feature = "db-diesel-postgres", not(feature = "db-diesel-sqlite")))]
pub type DieselConn = diesel::pg::PgConnection;
#[cfg(all(
feature = "db-diesel",
not(any(feature = "db-diesel-sqlite", feature = "db-diesel-postgres")),
))]
compile_error!("enable a Diesel backend: db-diesel-sqlite or db-diesel-postgres");
type Pool = r2d2::Pool<diesel::r2d2::ConnectionManager<DieselConn>>;
#[derive(Clone)]
pub struct DieselBlockingPool {
pool: Arc<Pool>,
}
impl DieselBlockingPool {
pub fn new(database_url: &str, max_size: u32) -> Result<Self, DataError> {
let manager = diesel::r2d2::ConnectionManager::<DieselConn>::new(database_url);
let pool = r2d2::Pool::builder()
.max_size(max_size)
.build(manager)
.map_err(|e| DataError::config(format!("diesel pool build failed: {e}")))?;
Ok(Self {
pool: Arc::new(pool),
})
}
pub async fn run<R, F>(&self, job: F) -> Result<R, DataError>
where
F: FnOnce(&mut DieselConn) -> Result<R, diesel::result::Error> + Send + 'static,
R: Send + 'static,
{
let pool = self.pool.clone();
tokio::task::spawn_blocking(move || {
let mut conn = pool
.get()
.map_err(|e| DataError::connection(e.to_string()))?;
job(&mut conn).map_err(|e| DataError::query(e.to_string()))
})
.await
.map_err(|e| DataError::other(format!("blocking task failed: {e}")))?
}
pub async fn ping(&self) -> Result<(), DataError> {
let pool = self.pool.clone();
tokio::task::spawn_blocking(move || {
pool.get()
.map(|_| ())
.map_err(|e| DataError::connection(e.to_string()))
})
.await
.map_err(|e| DataError::other(format!("blocking task failed: {e}")))?
}
pub async fn transaction<R, F>(&self, job: F) -> Result<R, DataError>
where
F: FnOnce(&mut DieselConn) -> Result<R, diesel::result::Error> + Send + 'static,
R: Send + 'static,
{
use diesel::Connection;
self.run(move |conn| conn.transaction(job)).await
}
}