use std::{fmt, future::Future, pin::Pin};
use super::{Manager, Metrics, ObjectInner, PoolError};
pub type HookResult<E> = Result<(), HookError<E>>;
pub type HookFuture<'a, E> = Pin<Box<dyn Future<Output = HookResult<E>> + Send + 'a>>;
type SyncFn<M> =
dyn Fn(&mut <M as Manager>::Type, &Metrics) -> HookResult<<M as Manager>::Error> + Sync + Send;
type AsyncFn<M> = dyn for<'a> Fn(&'a mut <M as Manager>::Type, &'a Metrics) -> HookFuture<'a, <M as Manager>::Error>
+ Sync
+ Send;
pub enum Hook<M: Manager> {
Fn(Box<SyncFn<M>>),
AsyncFn(Box<AsyncFn<M>>),
}
impl<M: Manager> Hook<M> {
pub fn sync_fn(
f: impl Fn(&mut M::Type, &Metrics) -> HookResult<M::Error> + Sync + Send + 'static,
) -> Self {
Self::Fn(Box::new(f))
}
pub fn async_fn(
f: impl for<'a> Fn(&'a mut M::Type, &'a Metrics) -> HookFuture<'a, M::Error>
+ Sync
+ Send
+ 'static,
) -> Self {
Self::AsyncFn(Box::new(f))
}
}
impl<M: Manager> fmt::Debug for Hook<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Fn(_) => f
.debug_tuple("Fn")
.finish(),
Self::AsyncFn(_) => f
.debug_tuple("AsyncFn")
.finish(),
}
}
}
#[derive(Debug)]
pub enum HookError<E> {
Continue(Option<HookErrorCause<E>>),
Abort(HookErrorCause<E>),
}
#[derive(Debug)]
pub enum HookErrorCause<E> {
Message(String),
StaticMessage(&'static str),
Backend(E),
}
impl<E> HookError<E> {
pub fn cause(&self) -> Option<&HookErrorCause<E>> {
match self {
Self::Continue(option) => option.as_ref(),
Self::Abort(cause) => Some(cause),
}
}
}
impl<E: fmt::Display> fmt::Display for HookError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.cause() {
Some(HookErrorCause::Message(msg)) => write!(f, "{}", msg),
Some(HookErrorCause::StaticMessage(msg)) => write!(f, "{}", msg),
Some(HookErrorCause::Backend(e)) => write!(f, "{}", e),
None => write!(f, "No cause given"),
}
}
}
impl<E: std::error::Error + 'static> std::error::Error for HookError<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self.cause() {
Some(HookErrorCause::Message(_)) => None,
Some(HookErrorCause::StaticMessage(_)) => None,
Some(HookErrorCause::Backend(e)) => Some(e),
None => None,
}
}
}
pub(crate) struct HookVec<M: Manager> {
vec: Vec<Hook<M>>,
}
impl<M: Manager> fmt::Debug for HookVec<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("HookVec")
.finish_non_exhaustive()
}
}
impl<M: Manager> Default for HookVec<M> {
fn default() -> Self {
Self { vec: Vec::new() }
}
}
impl<M: Manager> HookVec<M> {
pub(crate) async fn apply(
&self,
inner: &mut ObjectInner<M>,
error: fn(e: HookError<M::Error>) -> PoolError<M::Error>,
) -> Result<Option<HookError<M::Error>>, PoolError<M::Error>> {
for hook in &self.vec {
let result = match hook {
Hook::Fn(f) => f(&mut inner.obj, &inner.metrics),
Hook::AsyncFn(f) => f(&mut inner.obj, &inner.metrics).await,
};
match result {
Ok(()) => {}
Err(e) => match e {
HookError::Continue(_) => return Ok(Some(e)),
HookError::Abort(_) => return Err(error(e)),
},
}
}
Ok(None)
}
pub(crate) fn push(&mut self, hook: Hook<M>) {
self.vec.push(hook);
}
}
pub(crate) struct Hooks<M: Manager> {
pub(crate) post_create: HookVec<M>,
pub(crate) pre_recycle: HookVec<M>,
pub(crate) post_recycle: HookVec<M>,
}
impl<M: Manager> fmt::Debug for Hooks<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Hooks")
.field("post_create", &self.post_create)
.field("pre_recycle", &self.post_recycle)
.field("post_recycle", &self.post_recycle)
.finish()
}
}
impl<M: Manager> Default for Hooks<M> {
fn default() -> Self {
Self {
pre_recycle: HookVec::default(),
post_create: HookVec::default(),
post_recycle: HookVec::default(),
}
}
}