#![allow(clippy::module_name_repetitions)]
use crate::{Lease, Pool};
use core::future::Future;
use core::pin::Pin;
use std::sync::Arc;
pub struct InitPool<T: Send + Sync + 'static, I: Init> {
pool: Pool<T>,
init: Arc<I>,
}
pub trait Init {
type Output;
fn call(&self) -> Self::Output;
}
pub struct InitFn<T>(Box<dyn Fn() -> T + Send + Sync + 'static>);
impl<T> Init for InitFn<T> {
type Output = T;
fn call(&self) -> Self::Output {
(self.0)()
}
}
impl<F, T> From<F> for InitFn<T>
where
F: Send + Sync + 'static + Fn() -> T,
{
fn from(f: F) -> Self {
Self(Box::new(f))
}
}
pub struct InitFnAsync<T>(Box<dyn Fn() -> Pin<Box<dyn Future<Output = T> + Send + 'static>> + Send>);
impl<T> Init for InitFnAsync<T> {
type Output = Pin<Box<dyn Future<Output = T> + Send + 'static>>;
fn call(&self) -> Self::Output {
(self.0)()
}
}
impl<T, F> From<F> for InitFnAsync<T>
where
F: Fn() -> Pin<Box<dyn Future<Output = T> + Send + 'static>> + Send + 'static,
{
fn from(f: F) -> Self {
Self(Box::new(f))
}
}
pub struct InitTryFn<T, E>(Box<dyn Fn() -> Result<T, E> + Send>);
impl<T, E> Init for InitTryFn<T, E> {
type Output = Result<T, E>;
fn call(&self) -> Self::Output {
(self.0)()
}
}
impl<F, T, E> From<F> for InitTryFn<T, E>
where
F: Fn() -> Result<T, E> + Send + 'static,
{
fn from(f: F) -> Self {
Self(Box::new(f))
}
}
#[allow(clippy::type_complexity)]
pub struct InitTryFnAsync<T, E>(Box<dyn Fn() -> Pin<Box<dyn Future<Output = Result<T, E>> + Send + 'static>> + Send>);
impl<T, E> Init for InitTryFnAsync<T, E> {
type Output = Pin<Box<dyn Future<Output = Result<T, E>>>>;
fn call(&self) -> Self::Output {
(self.0)()
}
}
impl<T, E, F> From<F> for InitTryFnAsync<T, E>
where
F: Fn() -> Pin<Box<dyn Future<Output = Result<T, E>> + Send + 'static>> + Send + 'static,
{
fn from(f: F) -> Self {
Self(Box::new(f))
}
}
impl<T: Send + Sync + 'static, I: Init> InitPool<T, I> {
pub fn try_into_locked_pool(self) -> Result<super::LockedPool<T>, (Pool<T>, super::PoolConversionError)> {
self.pool.try_into_locked_pool()
}
#[inline]
pub fn new(init: I) -> Self {
Self {
pool: Pool::default(),
init: Arc::new(init),
}
}
#[inline]
pub fn new_from<O: Into<I>>(init: O) -> Self {
Self::new(init.into())
}
pub(super) fn new_from_pool(pool: Pool<T>, init: I) -> Self {
Self {
pool,
init: Arc::new(init),
}
}
#[allow(clippy::must_use_candidate)]
#[inline]
pub fn init(&self) -> I::Output {
self.init.call()
}
#[must_use]
#[inline]
pub fn try_get(&self) -> Option<Lease<T>> {
self.pool.try_get()
}
#[cfg(feature = "async")]
#[inline]
pub async fn get(&self) -> Lease<T> {
self.pool.get().await
}
#[cfg(feature = "async")]
#[inline]
pub fn stream(&self) -> impl futures_core::Stream<Item = Lease<T>> {
crate::PoolStream::new(&self.pool)
}
#[inline]
pub fn try_get_or_new(&self) -> Lease<T>
where
I::Output: Into<T>,
{
let lease = self.pool.try_get_or_new(|| self.init.call().into());
lease
}
#[inline]
pub async fn get_or_new(&self) -> Lease<T>
where
I::Output: Future<Output = T>,
{
self.pool.get_or_new(|| self.init.call()).await
}
#[inline]
pub fn try_get_or_try_new<E>(&self) -> Result<Lease<T>, E>
where
I::Output: Into<Result<T, E>>,
{
self.pool.try_get_or_try_new(|| self.init.call().into())
}
#[inline]
pub async fn get_or_try_new<E>(&self) -> Result<Lease<T>, E>
where
I::Output: Future<Output = Result<T, E>>,
{
self.pool.get_or_try_new(|| self.init.call()).await
}
#[must_use]
pub fn try_get_or_new_with_cap(&self, cap: usize) -> Option<Lease<T>>
where
I::Output: Into<T>,
{
self.pool.try_get_or_new_with_cap(cap, || self.init.call().into())
}
#[inline]
#[cfg(feature = "async")]
pub async fn get_or_new_with_cap(&self, cap: usize) -> Lease<T>
where
I::Output: Future<Output = T>,
{
self.pool.get_or_new_with_cap(cap, || self.init.call()).await
}
#[inline]
pub fn try_get_or_try_new_with_cap<E>(&self, cap: usize) -> Result<Option<Lease<T>>, E>
where
I::Output: Into<Result<T, E>>,
{
self.pool.try_get_or_try_new_with_cap(cap, || self.init.call().into())
}
#[inline]
#[cfg(feature = "async")]
pub async fn get_or_try_new_with_cap<E>(&self, cap: usize) -> Result<Lease<T>, E>
where
I::Output: Future<Output = Result<T, E>>,
{
self.pool.get_or_try_new_with_cap(cap, || self.init.call()).await
}
#[must_use]
#[inline]
pub fn len(&self) -> usize {
self.pool.len()
}
#[inline]
pub fn clear(&self) {
self.pool.clear();
}
#[must_use]
#[inline]
pub fn available(&self) -> usize {
self.pool.available()
}
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool {
self.pool.is_empty()
}
#[inline]
pub fn disassociate(&self, lease: &Lease<T>) {
self.pool.disassociate(lease);
}
}
impl<T: Send + Sync + 'static, I: Init> Clone for InitPool<T, I> {
fn clone(&self) -> Self {
Self {
pool: self.pool.clone(),
init: self.init.clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures::*;
#[test]
fn test_sync() {
let init_pool = InitPool::<u8, InitFn<_>>::new_from(|| 42_u8);
assert!(init_pool.try_get().is_none());
let lease = init_pool.try_get_or_new();
assert_eq!(*lease, 42);
}
#[tokio::test]
async fn test() {
let init_pool = InitPool::<u8, InitFnAsync<_>>::new_from(|| async { 42_u8 }.boxed());
assert!(init_pool.try_get().is_none());
assert_eq!(*init_pool.get_or_new().await, 42);
}
#[test]
fn test_try_sync() {
let init_pool = InitPool::<u8, InitTryFn<_, core::convert::Infallible>>::new_from(|| Ok(42_u8));
assert!(init_pool.try_get().is_none());
assert_eq!(*init_pool.try_get_or_try_new().unwrap(), 42);
}
#[tokio::test]
async fn test_try_async() {
let init_pool = InitPool::<u8, InitTryFnAsync<_, core::convert::Infallible>>::new_from(|| async { Ok(42_u8) }.boxed());
assert!(init_pool.try_get().is_none());
assert_eq!(*init_pool.get_or_try_new().await.unwrap(), 42);
}
#[tokio::test]
async fn test_clone() {
let init_pool = InitPool::<u8, InitTryFnAsync<_, core::convert::Infallible>>::new_from(|| {
async { Ok::<_, core::convert::Infallible>(42_u8) }.boxed()
});
let init_pool_2 = init_pool.clone();
assert!(init_pool.try_get().is_none());
assert_eq!(init_pool.len(), 0);
assert_eq!(init_pool_2.len(), 0);
let value = init_pool.get_or_try_new().await.unwrap();
assert_eq!(*value, 42);
assert_eq!(init_pool.len(), 1);
assert_eq!(init_pool_2.len(), 1);
assert_eq!(init_pool.available(), 0);
assert_eq!(init_pool_2.available(), 0);
drop(value);
assert_eq!(init_pool.available(), 1);
assert_eq!(init_pool_2.available(), 1);
}
}