deadpool/managed/
hooks.rs

1//! Hooks allowing to run code when creating and/or recycling objects.
2
3use std::{fmt, future::Future, pin::Pin};
4
5use super::{Manager, Metrics, ObjectInner, PoolError};
6
7/// The result returned by hooks
8pub type HookResult<E> = Result<(), HookError<E>>;
9
10/// The boxed future that should be returned by async hooks
11pub type HookFuture<'a, E> = Pin<Box<dyn Future<Output = HookResult<E>> + Send + 'a>>;
12
13/// Function signature for sync callbacks
14type SyncFn<M> =
15    dyn Fn(&mut <M as Manager>::Type, &Metrics) -> HookResult<<M as Manager>::Error> + Sync + Send;
16
17/// Function siganture for async callbacks
18type AsyncFn<M> = dyn for<'a> Fn(&'a mut <M as Manager>::Type, &'a Metrics) -> HookFuture<'a, <M as Manager>::Error>
19    + Sync
20    + Send;
21
22/// Wrapper for hook functions
23pub enum Hook<M: Manager> {
24    /// Use a plain function (non-async) as a hook
25    Fn(Box<SyncFn<M>>),
26    /// Use an async function as a hook
27    AsyncFn(Box<AsyncFn<M>>),
28}
29
30impl<M: Manager> Hook<M> {
31    /// Create Hook from sync function
32    pub fn sync_fn(
33        f: impl Fn(&mut M::Type, &Metrics) -> HookResult<M::Error> + Sync + Send + 'static,
34    ) -> Self {
35        Self::Fn(Box::new(f))
36    }
37    /// Create Hook from async function
38    pub fn async_fn(
39        f: impl for<'a> Fn(&'a mut M::Type, &'a Metrics) -> HookFuture<'a, M::Error>
40            + Sync
41            + Send
42            + 'static,
43    ) -> Self {
44        Self::AsyncFn(Box::new(f))
45    }
46}
47
48impl<M: Manager> fmt::Debug for Hook<M> {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        match self {
51            Self::Fn(_) => f
52                .debug_tuple("Fn")
53                //.field(arg0)
54                .finish(),
55            Self::AsyncFn(_) => f
56                .debug_tuple("AsyncFn")
57                //.field(arg0)
58                .finish(),
59        }
60    }
61}
62
63/// Error structure which which can abort the creation and recycling
64/// of objects.
65///
66/// There are two variants [`HookError::Continue`] tells the pool
67/// to continue the running [`Pool`] operation ([`get`],
68/// [`timeout_get`] or [`try_get`]) while [`HookError::Abort`] does abort
69/// that operation with an error.
70///
71/// [`Pool`]: crate::managed::Pool
72/// [`get`]: crate::managed::Pool::get
73/// [`timeout_get`]: crate::managed::Pool::timeout_get
74/// [`try_get`]: crate::managed::Pool::try_get
75#[derive(Debug)]
76pub enum HookError<E> {
77    /// This variant can be returned by hooks if the object should be
78    /// discarded but the operation should be continued.
79    Continue(Option<HookErrorCause<E>>),
80    /// This variant causes the object to be discarded and aborts the
81    /// operation.
82    Abort(HookErrorCause<E>),
83}
84
85/// Possible errors returned by hooks
86#[derive(Debug)]
87pub enum HookErrorCause<E> {
88    /// Hook failed for some other reason.
89    Message(String),
90
91    /// Hook failed for some other reason.
92    StaticMessage(&'static str),
93
94    /// Error caused by the backend.
95    Backend(E),
96}
97
98impl<E> HookError<E> {
99    /// Get optional cause of this error
100    pub fn cause(&self) -> Option<&HookErrorCause<E>> {
101        match self {
102            Self::Continue(option) => option.as_ref(),
103            Self::Abort(cause) => Some(cause),
104        }
105    }
106}
107
108impl<E: fmt::Display> fmt::Display for HookError<E> {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        match self.cause() {
111            Some(HookErrorCause::Message(msg)) => write!(f, "{}", msg),
112            Some(HookErrorCause::StaticMessage(msg)) => write!(f, "{}", msg),
113            Some(HookErrorCause::Backend(e)) => write!(f, "{}", e),
114            None => write!(f, "No cause given"),
115        }
116    }
117}
118
119impl<E: std::error::Error + 'static> std::error::Error for HookError<E> {
120    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
121        match self.cause() {
122            Some(HookErrorCause::Message(_)) => None,
123            Some(HookErrorCause::StaticMessage(_)) => None,
124            Some(HookErrorCause::Backend(e)) => Some(e),
125            None => None,
126        }
127    }
128}
129
130pub(crate) struct HookVec<M: Manager> {
131    vec: Vec<Hook<M>>,
132}
133
134// Implemented manually to avoid unnecessary trait bound on `M` type parameter.
135impl<M: Manager> fmt::Debug for HookVec<M> {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        f.debug_struct("HookVec")
138            //.field("fns", &self.fns)
139            .finish_non_exhaustive()
140    }
141}
142
143// Implemented manually to avoid unnecessary trait bound on `M` type parameter.
144impl<M: Manager> Default for HookVec<M> {
145    fn default() -> Self {
146        Self { vec: Vec::new() }
147    }
148}
149
150impl<M: Manager> HookVec<M> {
151    pub(crate) async fn apply(
152        &self,
153        inner: &mut ObjectInner<M>,
154        error: fn(e: HookError<M::Error>) -> PoolError<M::Error>,
155    ) -> Result<Option<HookError<M::Error>>, PoolError<M::Error>> {
156        for hook in &self.vec {
157            let result = match hook {
158                Hook::Fn(f) => f(&mut inner.obj, &inner.metrics),
159                Hook::AsyncFn(f) => f(&mut inner.obj, &inner.metrics).await,
160            };
161            match result {
162                Ok(()) => {}
163                Err(e) => match e {
164                    HookError::Continue(_) => return Ok(Some(e)),
165                    HookError::Abort(_) => return Err(error(e)),
166                },
167            }
168        }
169        Ok(None)
170    }
171    pub(crate) fn push(&mut self, hook: Hook<M>) {
172        self.vec.push(hook);
173    }
174}
175
176/// Collection of all the hooks that can be configured for a [`Pool`].
177///
178/// [`Pool`]: super::Pool
179pub(crate) struct Hooks<M: Manager> {
180    pub(crate) post_create: HookVec<M>,
181    pub(crate) pre_recycle: HookVec<M>,
182    pub(crate) post_recycle: HookVec<M>,
183}
184
185// Implemented manually to avoid unnecessary trait bound on `M` type parameter.
186impl<M: Manager> fmt::Debug for Hooks<M> {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        f.debug_struct("Hooks")
189            .field("post_create", &self.post_create)
190            .field("pre_recycle", &self.post_recycle)
191            .field("post_recycle", &self.post_recycle)
192            .finish()
193    }
194}
195
196// Implemented manually to avoid unnecessary trait bound on `M` type parameter.
197impl<M: Manager> Default for Hooks<M> {
198    fn default() -> Self {
199        Self {
200            pre_recycle: HookVec::default(),
201            post_create: HookVec::default(),
202            post_recycle: HookVec::default(),
203        }
204    }
205}