deadpool/managed/
builder.rs

1use std::{fmt, marker::PhantomData, time::Duration};
2
3use crate::Runtime;
4
5use super::{
6    hooks::{Hook, Hooks},
7    Manager, Object, Pool, PoolConfig, QueueMode, Timeouts,
8};
9
10/// Possible errors returned when [`PoolBuilder::build()`] fails to build a
11/// [`Pool`].
12#[derive(Copy, Clone, Debug)]
13pub enum BuildError {
14    /// [`Runtime`] is required du to configured timeouts.
15    NoRuntimeSpecified,
16}
17
18impl fmt::Display for BuildError {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        match self {
21            Self::NoRuntimeSpecified => write!(
22                f,
23                "Error occurred while building the pool: Timeouts require a runtime",
24            ),
25        }
26    }
27}
28
29impl std::error::Error for BuildError {
30    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
31        match self {
32            Self::NoRuntimeSpecified => None,
33        }
34    }
35}
36
37/// Builder for [`Pool`]s.
38///
39/// Instances of this are created by calling the [`Pool::builder()`] method.
40#[must_use = "builder does nothing itself, use `.build()` to build it"]
41pub struct PoolBuilder<M, W = Object<M>>
42where
43    M: Manager,
44    W: From<Object<M>>,
45{
46    pub(crate) manager: M,
47    pub(crate) config: PoolConfig,
48    pub(crate) runtime: Option<Runtime>,
49    pub(crate) hooks: Hooks<M>,
50    _wrapper: PhantomData<fn() -> W>,
51}
52
53// Implemented manually to avoid unnecessary trait bound on `W` type parameter.
54impl<M, W> fmt::Debug for PoolBuilder<M, W>
55where
56    M: fmt::Debug + Manager,
57    W: From<Object<M>>,
58{
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        f.debug_struct("PoolBuilder")
61            .field("manager", &self.manager)
62            .field("config", &self.config)
63            .field("runtime", &self.runtime)
64            .field("hooks", &self.hooks)
65            .field("_wrapper", &self._wrapper)
66            .finish()
67    }
68}
69
70impl<M, W> PoolBuilder<M, W>
71where
72    M: Manager,
73    W: From<Object<M>>,
74{
75    pub(crate) fn new(manager: M) -> Self {
76        Self {
77            manager,
78            config: PoolConfig::default(),
79            runtime: None,
80            hooks: Hooks::default(),
81            _wrapper: PhantomData,
82        }
83    }
84
85    /// Builds the [`Pool`].
86    ///
87    /// # Errors
88    ///
89    /// See [`BuildError`] for details.
90    pub fn build(self) -> Result<Pool<M, W>, BuildError> {
91        // Return an error if a timeout is configured without runtime.
92        let t = &self.config.timeouts;
93        if (t.wait.is_some() || t.create.is_some() || t.recycle.is_some()) && self.runtime.is_none()
94        {
95            return Err(BuildError::NoRuntimeSpecified);
96        }
97        Ok(Pool::from_builder(self))
98    }
99
100    /// Sets a [`PoolConfig`] to build the [`Pool`] with.
101    pub fn config(mut self, value: PoolConfig) -> Self {
102        self.config = value;
103        self
104    }
105
106    /// Sets the [`PoolConfig::max_size`].
107    pub fn max_size(mut self, value: usize) -> Self {
108        self.config.max_size = value;
109        self
110    }
111
112    /// Sets the [`PoolConfig::timeouts`].
113    pub fn timeouts(mut self, value: Timeouts) -> Self {
114        self.config.timeouts = value;
115        self
116    }
117
118    /// Sets the [`Timeouts::wait`] value of the [`PoolConfig::timeouts`].
119    pub fn wait_timeout(mut self, value: Option<Duration>) -> Self {
120        self.config.timeouts.wait = value;
121        self
122    }
123
124    /// Sets the [`Timeouts::create`] value of the [`PoolConfig::timeouts`].
125    pub fn create_timeout(mut self, value: Option<Duration>) -> Self {
126        self.config.timeouts.create = value;
127        self
128    }
129
130    /// Sets the [`Timeouts::recycle`] value of the [`PoolConfig::timeouts`].
131    pub fn recycle_timeout(mut self, value: Option<Duration>) -> Self {
132        self.config.timeouts.recycle = value;
133        self
134    }
135
136    /// Sets the [`PoolConfig::queue_mode`].
137    pub fn queue_mode(mut self, value: QueueMode) -> Self {
138        self.config.queue_mode = value;
139        self
140    }
141
142    /// Attaches a `post_create` hook.
143    ///
144    /// The given `hook` will be called each time right after a new [`Object`]
145    /// has been created.
146    pub fn post_create(mut self, hook: impl Into<Hook<M>>) -> Self {
147        self.hooks.post_create.push(hook.into());
148        self
149    }
150
151    /// Attaches a `pre_recycle` hook.
152    ///
153    /// The given `hook` will be called each time right before an [`Object`] will
154    /// be recycled.
155    pub fn pre_recycle(mut self, hook: impl Into<Hook<M>>) -> Self {
156        self.hooks.pre_recycle.push(hook.into());
157        self
158    }
159
160    /// Attaches a `post_recycle` hook.
161    ///
162    /// The given `hook` will be called each time right after an [`Object`] has
163    /// been recycled.
164    pub fn post_recycle(mut self, hook: impl Into<Hook<M>>) -> Self {
165        self.hooks.post_recycle.push(hook.into());
166        self
167    }
168
169    /// Sets the [`Runtime`].
170    ///
171    /// # Important
172    ///
173    /// The [`Runtime`] is optional. Most [`Pool`]s don't need a
174    /// [`Runtime`]. If want to utilize timeouts, however a [`Runtime`] must be
175    /// specified as you will otherwise get a [`PoolError::NoRuntimeSpecified`]
176    /// when trying to use [`Pool::timeout_get()`].
177    ///
178    /// [`PoolBuilder::build()`] will fail with a
179    /// [`BuildError::NoRuntimeSpecified`] if you try to build a
180    /// [`Pool`] with timeouts and no [`Runtime`] specified.
181    ///
182    /// [`PoolError::NoRuntimeSpecified`]: super::PoolError::NoRuntimeSpecified
183    pub fn runtime(mut self, value: Runtime) -> Self {
184        self.runtime = Some(value);
185        self
186    }
187}