use std::{
marker::PhantomData,
task::{Context, Poll},
};
use axum_core::{
extract::Request,
response::{IntoResponse, Response},
};
#[cfg(feature = "bb8")]
use bb8_postgres::tokio_postgres;
#[cfg(feature = "deadpool")]
use deadpool_postgres::tokio_postgres;
use futures_core::future::BoxFuture;
use super::{Pool, extension::Extension};
pub struct Layer<E> {
pool: Pool,
_error: PhantomData<E>,
}
impl<E> Clone for Layer<E> {
fn clone(&self) -> Self {
Self {
pool: self.pool.clone(),
_error: self._error,
}
}
}
impl From<Pool> for Layer<super::Error> {
fn from(value: Pool) -> Self {
Self {
pool: value,
_error: PhantomData,
}
}
}
impl<S, E> tower_layer::Layer<S> for Layer<E> {
type Service = Service<S, E>;
fn layer(&self, inner: S) -> Self::Service {
Service {
pool: self.pool.clone(),
inner,
_error: self._error,
}
}
}
pub struct Service<S, E> {
pool: Pool,
inner: S,
_error: PhantomData<E>,
}
impl<S: Clone, E> Clone for Service<S, E> {
fn clone(&self) -> Self {
Self {
pool: self.pool.clone(),
inner: self.inner.clone(),
_error: self._error,
}
}
}
impl<S, E> tower_service::Service<Request> for Service<S, E>
where
S: tower_service::Service<Request, Response = Response> + Send + 'static,
S::Future: Send + 'static,
E: From<tokio_postgres::Error> + IntoResponse,
{
type Response = S::Response;
type Error = S::Error;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, mut req: Request) -> Self::Future {
let ext = Extension::from(self.pool.clone());
req.extensions_mut().insert(ext.clone());
let res = self.inner.call(req);
Box::pin(async move {
let res = res.await?;
if !res.status().is_server_error()
&& !res.status().is_client_error()
&& let Err(err) = ext.commit().await
{
return Ok(E::from(err).into_response());
}
Ok(res)
})
}
}