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