use std::error::Error;
use crate::core::errors::FactoryError;
pub trait ConnectionFactory<T>: Send + Sync {
fn create(&self) -> Result<T, FactoryError>;
fn validate(&self, _conn: &T) -> bool {
true
}
fn reset(&self, _conn: &T) -> Result<(), FactoryError> {
Ok(())
}
fn close(&self, _conn: &T) -> Result<(), FactoryError> {
Ok(())
}
fn is_fatal_error(&self, _err: &(dyn Error + 'static)) -> bool {
false
}
}
impl<T> ConnectionFactory<T> for Box<dyn ConnectionFactory<T>> {
fn create(&self) -> Result<T, FactoryError> {
(**self).create()
}
fn validate(&self, conn: &T) -> bool {
(**self).validate(conn)
}
fn reset(&self, conn: &T) -> Result<(), FactoryError> {
(**self).reset(conn)
}
fn close(&self, conn: &T) -> Result<(), FactoryError> {
(**self).close(conn)
}
fn is_fatal_error(&self, err: &(dyn Error + 'static)) -> bool {
(**self).is_fatal_error(err)
}
}
type CreateFn<T> = Box<dyn Fn() -> Result<T, FactoryError> + Send + Sync>;
type ValidateFn<T> = Box<dyn Fn(&T) -> bool + Send + Sync>;
type ResetFn<T> = Box<dyn Fn(&T) -> Result<(), FactoryError> + Send + Sync>;
type CloseFn<T> = Box<dyn Fn(&T) -> Result<(), FactoryError> + Send + Sync>;
type IsFatalFn = Box<dyn Fn(&(dyn Error + 'static)) -> bool + Send + Sync>;
pub struct FnFactory<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> FnFactory<T> {
pub fn new<F>(create: F) -> Self
where
F: Fn() -> 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: Fn(&T) -> bool + Send + Sync + 'static,
{
self.validate_fn = Some(Box::new(f));
self
}
pub fn with_reset<F>(mut self, f: F) -> Self
where
F: Fn(&T) -> 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: Fn(&T) -> 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> ConnectionFactory<T> for FnFactory<T>
where
T: Send + Sync,
{
fn create(&self) -> Result<T, FactoryError> {
(self.create_fn)()
}
fn validate(&self, conn: &T) -> bool {
match &self.validate_fn {
Some(f) => f(conn),
None => true,
}
}
fn reset(&self, conn: &T) -> Result<(), FactoryError> {
match &self.reset_fn {
Some(f) => f(conn),
None => Ok(()),
}
}
fn close(&self, conn: &T) -> Result<(), FactoryError> {
match &self.close_fn {
Some(f) => f(conn),
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::*;
#[test]
fn defaults_apply_when_unset() {
let f = FnFactory::new(|| Ok::<_, FactoryError>(7u32));
assert_eq!(f.create().unwrap(), 7);
assert!(f.validate(&7));
assert!(f.reset(&7).is_ok());
assert!(f.close(&7).is_ok());
let err: FactoryError = "x".into();
assert!(!f.is_fatal_error(&*err));
}
#[test]
fn custom_hooks_are_used() {
let f = FnFactory::new(|| Ok::<_, FactoryError>(1u32))
.with_validate(|c| *c == 1)
.with_reset(|_| Err::<(), FactoryError>("reset boom".into()))
.with_close(|_| Err::<(), FactoryError>("close boom".into()))
.with_is_fatal_error(|e| e.to_string() == "fatal");
assert!(f.validate(&1));
assert!(!f.validate(&2));
assert_eq!(f.reset(&1).unwrap_err().to_string(), "reset boom");
assert_eq!(f.close(&1).unwrap_err().to_string(), "close boom");
let fatal: FactoryError = "fatal".into();
let ok: FactoryError = "meh".into();
assert!(f.is_fatal_error(&*fatal));
assert!(!f.is_fatal_error(&*ok));
}
#[test]
fn create_can_fail() {
let f = FnFactory::new(|| Err::<u32, FactoryError>("down".into()));
assert_eq!(f.create().unwrap_err().to_string(), "down");
}
#[test]
fn trait_defaults_apply_to_a_minimal_impl() {
struct Minimal;
impl ConnectionFactory<u32> for Minimal {
fn create(&self) -> Result<u32, FactoryError> {
Ok(1)
}
}
let m = Minimal;
assert_eq!(m.create().unwrap(), 1);
assert!(m.validate(&1));
assert!(m.reset(&1).is_ok());
assert!(m.close(&1).is_ok());
let e: FactoryError = "x".into();
assert!(!m.is_fatal_error(&*e));
}
#[test]
fn boxed_factory_delegates() {
let boxed: Box<dyn ConnectionFactory<u32>> = Box::new(
FnFactory::new(|| Ok::<_, FactoryError>(5u32))
.with_validate(|c| *c == 5)
.with_reset(|_| Ok(()))
.with_close(|_| Ok(()))
.with_is_fatal_error(|_| true),
);
assert_eq!(boxed.create().unwrap(), 5);
assert!(boxed.validate(&5));
assert!(boxed.reset(&5).is_ok());
assert!(boxed.close(&5).is_ok());
let e: FactoryError = "x".into();
assert!(boxed.is_fatal_error(&*e));
}
}