use std::error::Error;
use std::future::Future;
use std::pin::Pin;
use crate::core::errors::FactoryError;
pub type BoxFuture<'a, O> = Pin<Box<dyn Future<Output = O> + Send + 'a>>;
pub trait AsyncConnectionFactory<T>: Send + Sync {
fn create(&self) -> impl Future<Output = Result<T, FactoryError>> + Send;
fn validate(&self, _conn: &T) -> impl Future<Output = bool> + Send {
async { true }
}
fn reset(&self, _conn: &T) -> impl Future<Output = Result<(), FactoryError>> + Send {
async { Ok(()) }
}
fn close(&self, _conn: &T) -> impl Future<Output = Result<(), FactoryError>> + Send {
async { Ok(()) }
}
fn is_fatal_error(&self, _err: &(dyn Error + 'static)) -> bool {
false
}
}
type CreateFn<T> = Box<dyn Fn() -> BoxFuture<'static, Result<T, FactoryError>> + Send + Sync>;
type ValidateFn<T> = Box<dyn for<'a> Fn(&'a T) -> BoxFuture<'a, bool> + Send + Sync>;
type ResetFn<T> =
Box<dyn for<'a> Fn(&'a T) -> BoxFuture<'a, Result<(), FactoryError>> + Send + Sync>;
type CloseFn<T> =
Box<dyn for<'a> Fn(&'a T) -> BoxFuture<'a, Result<(), FactoryError>> + Send + Sync>;
type IsFatalFn = Box<dyn Fn(&(dyn Error + 'static)) -> bool + Send + Sync>;
pub struct AsyncFnFactory<T> {
create_fn: CreateFn<T>,
validate_fn: Option<ValidateFn<T>>,
reset_fn: Option<ResetFn<T>>,
close_fn: Option<CloseFn<T>>,
is_fatal_fn: Option<IsFatalFn>,
}
impl<T> AsyncFnFactory<T> {
pub fn new<F>(create: F) -> Self
where
F: Fn() -> BoxFuture<'static, Result<T, FactoryError>> + Send + Sync + 'static,
{
Self {
create_fn: Box::new(create),
validate_fn: None,
reset_fn: None,
close_fn: None,
is_fatal_fn: None,
}
}
pub fn with_validate<F>(mut self, f: F) -> Self
where
F: for<'a> Fn(&'a T) -> BoxFuture<'a, bool> + Send + Sync + 'static,
{
self.validate_fn = Some(Box::new(f));
self
}
pub fn with_reset<F>(mut self, f: F) -> Self
where
F: for<'a> Fn(&'a T) -> BoxFuture<'a, Result<(), FactoryError>> + Send + Sync + 'static,
{
self.reset_fn = Some(Box::new(f));
self
}
pub fn with_close<F>(mut self, f: F) -> Self
where
F: for<'a> Fn(&'a T) -> BoxFuture<'a, Result<(), FactoryError>> + Send + Sync + 'static,
{
self.close_fn = Some(Box::new(f));
self
}
pub fn with_is_fatal_error<F>(mut self, f: F) -> Self
where
F: Fn(&(dyn Error + 'static)) -> bool + Send + Sync + 'static,
{
self.is_fatal_fn = Some(Box::new(f));
self
}
}
impl<T> AsyncConnectionFactory<T> for AsyncFnFactory<T>
where
T: Send + Sync,
{
fn create(&self) -> impl Future<Output = Result<T, FactoryError>> + Send {
(self.create_fn)()
}
async fn validate(&self, conn: &T) -> bool {
match &self.validate_fn {
Some(f) => f(conn).await,
None => true,
}
}
async fn reset(&self, conn: &T) -> Result<(), FactoryError> {
match &self.reset_fn {
Some(f) => f(conn).await,
None => Ok(()),
}
}
async fn close(&self, conn: &T) -> Result<(), FactoryError> {
match &self.close_fn {
Some(f) => f(conn).await,
None => Ok(()),
}
}
fn is_fatal_error(&self, err: &(dyn Error + 'static)) -> bool {
match &self.is_fatal_fn {
Some(f) => f(err),
None => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn defaults_apply_when_unset() {
let f = AsyncFnFactory::<u32>::new(|| Box::pin(async { Ok::<_, FactoryError>(7u32) }));
assert_eq!(f.create().await.unwrap(), 7u32);
assert!(f.validate(&7).await);
assert!(f.reset(&7).await.is_ok());
assert!(f.close(&7).await.is_ok());
let err: FactoryError = "x".into();
assert!(!f.is_fatal_error(&*err));
}
#[tokio::test]
async fn custom_hooks_are_used() {
let f = AsyncFnFactory::<u32>::new(|| Box::pin(async { Ok::<_, FactoryError>(1u32) }))
.with_validate(|c: &u32| {
let v = *c == 1;
Box::pin(async move { v }) as _
})
.with_reset(|_| Box::pin(async { Err::<(), FactoryError>("reset boom".into()) }) as _)
.with_close(|_| Box::pin(async { Err::<(), FactoryError>("close boom".into()) }) as _)
.with_is_fatal_error(|e| e.to_string() == "fatal");
assert!(f.validate(&1).await);
assert_eq!(f.reset(&1).await.unwrap_err().to_string(), "reset boom");
assert_eq!(f.close(&1).await.unwrap_err().to_string(), "close boom");
let fatal: FactoryError = "fatal".into();
assert!(f.is_fatal_error(&*fatal));
}
#[tokio::test]
async fn trait_defaults_apply_to_a_minimal_impl() {
struct Minimal;
impl AsyncConnectionFactory<u32> for Minimal {
async fn create(&self) -> Result<u32, FactoryError> {
Ok(1)
}
}
let m = Minimal;
assert_eq!(m.create().await.unwrap(), 1);
assert!(m.validate(&1).await);
assert!(m.reset(&1).await.is_ok());
assert!(m.close(&1).await.is_ok());
let e: FactoryError = "x".into();
assert!(!m.is_fatal_error(&*e));
}
}