use crate::{Locator, LocatorError, Provider};
use std::any::TypeId;
pub trait TryLocator: sealed::Sealed {
fn try_insert_with<F, T>(&mut self, factory: F) -> Option<Provider>
where
F: Fn(&Self) -> Result<T, LocatorError> + Send + Sync + 'static,
T: Send + Sync + 'static;
fn try_get<T>(&self) -> Result<T, LocatorError>
where
T: Send + Sync + 'static;
}
impl TryLocator for Locator {
fn try_insert_with<F, T>(&mut self, factory: F) -> Option<Provider>
where
F: Fn(&Self) -> Result<T, LocatorError> + 'static + Send + Sync,
T: Send + Sync + 'static,
{
let provider = Provider::Factory(Box::new(move |locator| {
let value = factory(locator);
Box::new(value)
}));
self.unchecked_insert(TypeId::of::<Result<T, LocatorError>>(), provider)
}
fn try_get<T>(&self) -> Result<T, LocatorError>
where
T: Send + Sync + 'static,
{
let provider = self
.unchecked_get(&TypeId::of::<Result<T, LocatorError>>())
.ok_or(LocatorError::NotFound {
expected: std::any::type_name::<T>(),
})?;
match provider {
Provider::Single(f) => {
let value = f();
value
.downcast::<Result<T, LocatorError>>()
.map(|x| *x)
.map_err(|_| LocatorError::NotFound {
expected: std::any::type_name::<T>(),
})
.and_then(std::convert::identity)
}
Provider::Factory(f) => {
let value = f(self);
value
.downcast::<Result<T, LocatorError>>()
.map(|x| *x)
.map_err(|_| LocatorError::NotFound {
expected: std::any::type_name::<T>(),
})
.and_then(std::convert::identity)
}
}
}
}
impl sealed::Sealed for Locator {}
pub(crate) mod sealed {
pub trait Sealed {}
}
#[cfg(test)]
mod tests {
use crate::{try_locator::TryLocator, Locator, LocatorError};
#[derive(Debug)]
struct ServiceA;
#[derive(Debug)]
struct ServiceB;
#[test]
fn test_try_insert_and_try_get() {
let mut locator = Locator::new();
locator.try_insert_with::<_, ServiceA>(|_| Ok(ServiceA));
let service_a = locator.try_get::<ServiceA>();
assert!(service_a.is_ok());
locator.try_insert_with::<_, ServiceB>(|_| Err(LocatorError::not_found::<ServiceB>()));
let service_b = locator.try_get::<ServiceB>();
assert!(service_b.is_err());
assert!(matches!(
service_b.unwrap_err(),
LocatorError::NotFound { .. }
));
}
}