#![feature(custom_attribute)]
#![feature(core_intrinsics)]
#![warn(missing_docs, clippy::dbg_macro, clippy::unimplemented)]
#![deny(
rust_2018_idioms,
future_incompatible,
missing_debug_implementations,
clippy::default_trait_access,
clippy::enum_glob_use,
clippy::needless_borrow,
clippy::large_digit_groups,
clippy::explicit_into_iter_loop
)]
pub use wonderbox_codegen::resolve_dependencies;
use crate::internal::AutoResolvable;
use core::any::TypeId;
use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Default, Debug, Clone)]
pub struct Container {
registered_types: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
}
type ImplementationFactory<T> = dyn Fn(&Container) -> T + Send + Sync;
impl Container {
pub fn new() -> Self {
Self::default()
}
pub fn register_clone<T>(&mut self, implementation: T) -> &mut Self
where
T: 'static + Send + Sync + Clone,
{
self.register_factory(move |_| implementation.clone());
self
}
pub fn register_default<T>(&mut self) -> &mut Self
where
T: 'static + Default,
{
self.register_factory(|_| T::default());
self
}
pub fn register_factory<T>(
&mut self,
implementation_factory: impl Fn(&Container) -> T + 'static + Send + Sync + Clone,
) -> &mut Self
where
T: 'static,
{
let registered_implementation_factory: Box<ImplementationFactory<T>> = {
let implementation_factory = implementation_factory.clone();
Box::new(move |container| implementation_factory(container))
};
self.registered_types.insert(
TypeId::of::<T>(),
Arc::new(registered_implementation_factory),
);
let partially_applied_implementation_factory: Box<
ImplementationFactory<Box<dyn Fn() -> T>>,
> = Box::new(move |container: &Container| {
let implementation_factory = implementation_factory.clone();
let container = container.clone();
Box::new(move || implementation_factory(&container))
});
self.registered_types.insert(
TypeId::of::<Box<dyn Fn() -> T>>(),
Arc::new(partially_applied_implementation_factory),
);
self
}
pub fn register_autoresolved<ResolvedType, RegisteredType>(
&mut self,
registration_fn: impl Fn(Option<ResolvedType>) -> RegisteredType + 'static + Send + Sync + Clone,
) -> &mut Self
where
ResolvedType: AutoResolvable,
RegisteredType: 'static,
{
self.register_factory(move |container| registration_fn(ResolvedType::resolve(container)));
self
}
pub fn register_container(&mut self, container: Container) -> &mut Self {
self.registered_types
.extend(container.registered_types.into_iter());
self
}
pub fn resolve<T>(&self) -> Option<T>
where
T: 'static,
{
let type_id = TypeId::of::<T>();
let resolvable_type = self.registered_types.get(&type_id)?;
let implementation_factory = resolvable_type
.downcast_ref::<Box<ImplementationFactory<T>>>()
.unwrap_or_else(|| {
panic!(
"Internal error: Couldn't downcast stored implementation factory to resolved \
type \"{}\"",
type_name::<T>()
)
});
let value: T = implementation_factory(self);
Some(value)
}
}
#[macro_export]
macro_rules! register {
($container: ident, $implementation: ty) => {
$container.register_autoresolved(|implementation: Option<$implementation>| {
implementation.unwrap()
})
};
($container: ident, $implementation: ty as Box<$registration: ty>) => {
$container.register_autoresolved(|implementation: Option<$implementation>| {
Box::new(implementation.unwrap()) as Box<$registration>
})
};
}
fn type_name<T>() -> &'static str {
unsafe { std::intrinsics::type_name::<T>() }
}
#[doc(hidden)]
pub mod internal {
use super::*;
pub trait AutoResolvable: Sized {
fn resolve(container: &Container) -> Option<Self>;
}
}
#[cfg(test)]
#[allow(clippy::blacklisted_name)]
mod tests {
use super::*;
use std::rc::Rc;
use std::sync::Mutex;
#[test]
fn resolves_none_when_not_registered() {
let container = Container::new();
let resolved = container.resolve::<String>();
assert!(resolved.is_none())
}
#[test]
fn resolves_string() {
let mut container = Container::new();
container.register_clone(String::new());
let resolved = container.resolve::<String>();
assert!(resolved.is_some())
}
#[test]
fn resolves_default() {
let mut container = Container::new();
container.register_default::<String>();
let resolved = container.resolve::<String>();
assert!(resolved.is_some())
}
#[test]
fn resolves_factory_generated_from_default() {
let mut container = Container::new();
container.register_default::<String>();
let resolved = container.resolve::<Box<dyn Fn() -> String>>();
assert!(resolved.is_some())
}
#[test]
fn resolves_rc_of_trait_object() {
let mut container = Container::new();
container
.register_clone(Arc::new(Mutex::new(FooImpl::new())) as Arc<Mutex<dyn Foo + Send>>);
let resolved = container.resolve::<Arc<Mutex<dyn Foo + Send>>>();
assert!(resolved.is_some())
}
#[test]
fn generates_factory_of_rc_of_trait_object() {
let mut container = Container::new();
container
.register_clone(Arc::new(Mutex::new(FooImpl::new())) as Arc<Mutex<dyn Foo + Send>>);
let resolved = container.resolve::<Box<dyn Fn() -> Arc<Mutex<dyn Foo + Send>>>>();
assert!(resolved.is_some())
}
#[test]
fn resolves_factory_of_rc_of_trait_object() {
let mut container = Container::new();
let factory = |_container: &Container| Rc::new(FooImpl::new()) as Rc<dyn Foo>;
container.register_factory(factory);
let resolved = container.resolve::<Rc<dyn Foo>>();
assert!(resolved.is_some())
}
#[test]
fn resolves_factory_of_box_of_trait_object() {
let mut container = Container::new();
let factory = |_container: &Container| Box::new(FooImpl::new()) as Box<dyn Foo>;
container.register_factory(factory);
let resolved = container.resolve::<Box<dyn Foo>>();
assert!(resolved.is_some())
}
#[test]
fn resolves_boxed_factory_of_box_of_trait_object() {
let mut container = Container::new();
let factory = |_container: &Container| Box::new(FooImpl::new()) as Box<dyn Foo>;
let boxed_factory = Box::new(factory);
container.register_factory(boxed_factory);
let resolved = container.resolve::<Box<dyn Foo>>();
assert!(resolved.is_some())
}
#[test]
fn resolves_boxed_factory_with_clone_dependency() {
let mut container = Container::new();
container.register_clone("foo".to_string());
container.register_factory(|container| {
let dependency = container.resolve::<String>().unwrap();
let bar = BarImpl::new(dependency);
Box::new(bar) as Box<dyn Bar>
});
let resolved = container.resolve::<Box<dyn Bar>>();
assert!(resolved.is_some())
}
#[test]
fn resolves_boxed_factory_with_factory_dependency() {
let mut container = Container::new();
container.register_clone("foo".to_string());
container
.register_factory(|_container: &Container| Box::new(FooImpl::new()) as Box<dyn Foo>);
container.register_factory(|container| {
let clone_dependency = container.resolve::<String>().unwrap();
let _factory_dependency = container.resolve::<Box<dyn Foo>>().unwrap();
let bar = BarImpl::new(clone_dependency);
Box::new(bar) as Box<dyn Bar>
});
let resolved = container.resolve::<Box<dyn Bar>>();
assert!(resolved.is_some())
}
#[test]
fn generates_factory_from_type_with_dependency() {
let mut container = Container::new();
container.register_clone("foo".to_string());
container
.register_factory(|_container: &Container| Box::new(FooImpl::new()) as Box<dyn Foo>);
container.register_factory(|container| {
let clone_dependency = container.resolve::<String>().unwrap();
let _factory_dependency = container.resolve::<Box<dyn Foo>>().unwrap();
let bar = BarImpl::new(clone_dependency);
Box::new(bar) as Box<dyn Bar>
});
let resolved = container.resolve::<Box<dyn Fn() -> Box<dyn Bar>>>();
assert!(resolved.is_some())
}
#[test]
fn resolves_type_from_merged_containers() {
let mut first_container = Container::new();
first_container.register_clone("foo".to_string());
let mut second_container = Container::new();
second_container.register_factory(|container| {
let dependency = container.resolve::<String>().unwrap();
let bar = BarImpl::new(dependency);
Box::new(bar) as Box<dyn Bar>
});
first_container.register_container(second_container);
let resolved = first_container.resolve::<Box<dyn Bar>>();
assert!(resolved.is_some())
}
trait Foo {}
struct FooImpl {}
impl FooImpl {
fn new() -> Self {
FooImpl {}
}
}
impl Foo for FooImpl {}
trait Bar {}
struct BarImpl {
_stored_string: String,
}
impl BarImpl {
fn new(stored_string: String) -> Self {
BarImpl {
_stored_string: stored_string,
}
}
}
impl Bar for BarImpl {}
}