#![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
)]
use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Default, Debug, Clone)]
pub struct Container {
registered_types: HashMap<&'static str, Arc<dyn Any + Send + Sync>>,
registered_type_factories: HashMap<&'static str, 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<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(
type_name::<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_type_factories.insert(
type_name::<Box<dyn Fn() -> T>>(),
Arc::new(partially_applied_implementation_factory),
);
self
}
pub fn extend(&mut self, container: Container) -> &mut Self {
self.registered_types
.extend(container.registered_types.into_iter());
self
}
pub fn try_resolve<T>(&self) -> Option<T>
where
T: 'static,
{
let key = type_name::<T>();
let resolvable_type = self
.registered_types
.get(key)
.or_else(|| self.registered_type_factories.get(key))?;
let implementation_factory = resolvable_type
.downcast_ref::<Box<ImplementationFactory<T>>>()
.unwrap_or_else(|| {
panic!(
"Internal error: Couldn't downcast internally stored registered type to resolved \
type `{}`.\nYou've encountered a Wonderbox bug. Please consider opening an \
issue at https://github.com/jnferner/wonderbox/issues/new\nAdditional info: {}",
type_name::<T>(),
self.resolution_failure_help()
)
});
let value = implementation_factory(self);
Some(value)
}
pub fn resolve<T>(&self) -> T
where
T: 'static,
{
self.try_resolve::<T>().unwrap_or_else(|| {
panic!(
"Wonderbox failed to resolve the type `{}`.\nHelp: {}",
type_name::<T>(),
self.resolution_failure_help()
)
})
}
fn resolution_failure_help(&self) -> String {
if self.registered_types.is_empty() {
"No registered types were found.".into()
} else {
format!(
"The following registered types were found.\n{}{}",
self.pretty_print_registered_types(),
"In addition, the following factories were generated for each type `T`.\n- \
std::boxed::Box<dyn Fn() -> T>"
)
}
}
fn pretty_print_registered_types(&self) -> String {
const AVERAGE_LENGTH_OF_TYPE_NAME: usize = 20;
let initial_capacity = self.registered_types.len() * AVERAGE_LENGTH_OF_TYPE_NAME;
let mut registered_type_names = self.registered_type_names();
registered_type_names.sort();
registered_type_names
.iter()
.map(|type_name| format!("- {}\n", type_name))
.fold(String::with_capacity(initial_capacity), |acc, type_name| {
acc + &type_name
})
}
fn registered_type_names(&self) -> Vec<String> {
self.registered_types
.keys()
.map(|&key| String::from(key))
.collect()
}
}
fn type_name<T>() -> &'static str {
unsafe { std::intrinsics::type_name::<T>() }
}
#[cfg(test)]
#[allow(clippy::blacklisted_name)]
mod tests {
use super::*;
use std::rc::Rc;
#[test]
fn resolves_none_when_not_registered() {
let container = Container::new();
let resolved = container.try_resolve::<String>();
assert!(resolved.is_none())
}
#[test]
#[should_panic(expected = "Wonderbox failed to resolve the type \
`std::string::String`.\nHelp: No registered types were found.")]
fn panics_when_unwraping_type_that_is_not_registered() {
let container = Container::new();
let _resolved = container.resolve::<String>();
}
#[test]
#[should_panic(
expected = "Wonderbox failed to resolve the type `std::boxed::Box<dyn std::ops::Fn() -> \
std::boxed::Box<dyn tests::Foo>>`.\nHelp: No registered types were found."
)]
fn panics_when_unwraping_trait_object_that_is_not_registered() {
let container = Container::new();
let _resolved = container.resolve::<Box<dyn Fn() -> Box<dyn Foo>>>();
}
#[test]
#[should_panic(
expected = "Wonderbox failed to resolve the type `std::boxed::Box<dyn \
tests::Bar>`.\nHelp: The following registered types were found.\n- \
std::boxed::Box<dyn tests::Foo>\n- std::string::String\nIn addition, the \
following factories were generated for each type `T`.\n- std::boxed::Box<dyn \
Fn() -> T>"
)]
fn panics_when_unwraping_trait_object_that_is_not_registered_when_other_types_are_registered() {
let mut container = Container::new();
container.register(|_| "foo".to_string());
container.register(|_| Box::new(FooImpl::new()) as Box<dyn Foo>);
let _resolved = container.resolve::<Box<dyn Bar>>();
}
#[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);
let resolved = container.try_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);
let resolved = container.try_resolve::<Box<dyn Foo>>();
assert!(resolved.is_some())
}
#[test]
fn resolves_and_unwraps_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);
let _resolved = container.resolve::<Box<dyn Foo>>();
}
#[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(boxed_factory);
let resolved = container.try_resolve::<Box<dyn Foo>>();
assert!(resolved.is_some())
}
#[test]
fn resolves_boxed_factory_with_clone_dependency() {
let mut container = Container::new();
container.register(|_| "foo".to_string());
container.register(|container| {
let dependency = container.try_resolve::<String>().unwrap();
let bar = BarImpl::new(dependency);
Box::new(bar) as Box<dyn Bar>
});
let resolved = container.try_resolve::<Box<dyn Bar>>();
assert!(resolved.is_some())
}
#[test]
fn resolves_boxed_factory_with_factory_dependency() {
let mut container = Container::new();
container.register(|_| "foo".to_string());
container.register(|_container: &Container| Box::new(FooImpl::new()) as Box<dyn Foo>);
container.register(|container| {
let clone_dependency = container.try_resolve::<String>().unwrap();
let _factory_dependency = container.try_resolve::<Box<dyn Foo>>().unwrap();
let bar = BarImpl::new(clone_dependency);
Box::new(bar) as Box<dyn Bar>
});
let resolved = container.try_resolve::<Box<dyn Bar>>();
assert!(resolved.is_some())
}
#[test]
fn generates_factory_from_type_with_dependency() {
let mut container = Container::new();
container.register(|_| "foo".to_string());
container.register(|_container: &Container| Box::new(FooImpl::new()) as Box<dyn Foo>);
container.register(|container| {
let clone_dependency = container.try_resolve::<String>().unwrap();
let _factory_dependency = container.try_resolve::<Box<dyn Foo>>().unwrap();
let bar = BarImpl::new(clone_dependency);
Box::new(bar) as Box<dyn Bar>
});
let resolved = container.try_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(|_| "foo".to_string());
let mut second_container = Container::new();
second_container.register(|container| {
let dependency = container.try_resolve::<String>().unwrap();
let bar = BarImpl::new(dependency);
Box::new(bar) as Box<dyn Bar>
});
first_container.extend(second_container);
let resolved = first_container.try_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 {}
}