use futures::future::{ready, BoxFuture};
use std::{
any::{Any, TypeId},
collections::{BTreeMap, BTreeSet},
fmt::{Debug, Formatter, Write},
future::Future,
iter::FromIterator,
panic::Location,
sync::Arc,
};
use crate::Type;
#[derive(Default, Clone, Debug)]
pub struct DependencyMap {
pub(crate) map: BTreeMap<TypeId, Dependency>,
}
#[derive(Clone)]
pub(crate) struct Dependency {
pub(crate) type_name: &'static str,
inner: Arc<dyn Any + Send + Sync>,
}
impl Debug for Dependency {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Dependency").field("type_name", &self.type_name).finish_non_exhaustive()
}
}
impl PartialEq for DependencyMap {
fn eq(&self, other: &Self) -> bool {
let keys1 = self.map.keys();
let keys2 = other.map.keys();
keys1.len() == keys2.len() && keys1.zip(keys2).map(|(k1, k2)| k1 == k2).all(|x| x)
}
}
impl DependencyMap {
pub fn new() -> Self {
Self::default()
}
pub fn insert<T: Send + Sync + 'static>(&mut self, item: T) -> Option<Arc<T>> {
self.map
.insert(
TypeId::of::<T>(),
Dependency { type_name: std::any::type_name::<T>(), inner: Arc::new(item) },
)
.map(|dep| dep.inner.downcast().expect("Values are stored by `TypeId`"))
}
pub fn insert_container(&mut self, container: Self) {
self.map.extend(container.map);
}
pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<Arc<T>> {
self.map
.remove(&TypeId::of::<T>())
.map(|dep| dep.inner.downcast().expect("Values are stored by `TypeId`"))
}
pub fn get<V>(&self) -> Arc<V>
where
V: Send + Sync + 'static,
{
self.map
.get(&TypeId::of::<V>())
.unwrap_or_else(|| {
panic!(
"`{}` was requested, but not provided. Available types:\n{}",
std::any::type_name::<V>(),
self.available_types()
)
})
.clone()
.inner
.downcast::<V>()
.expect("Checked by `unwrap_or_else`")
}
pub fn try_get<V: Send + Sync + 'static>(&self) -> Option<Arc<V>> {
self.map
.get(&TypeId::of::<V>())
.cloned()
.map(|v| v.inner.downcast().expect("Values are stored by `TypeId`"))
}
fn available_types(&self) -> String {
let mut list = String::new();
for dep in self.map.values() {
writeln!(list, " `{}`", dep.type_name).unwrap();
}
list
}
}
pub trait Injectable<Output, FnArgs>
where
Output: 'static,
{
fn inject<'a>(&'a self, container: &'a DependencyMap) -> CompiledFn<'a, Output>;
fn input_types() -> BTreeSet<Type>;
#[track_caller]
fn obligations() -> BTreeMap<Type, &'static Location<'static>> {
let location = Location::caller();
Self::input_types().into_iter().map(|ty| (ty, location)).collect()
}
}
pub type CompiledFn<'a, Output> = Arc<dyn Fn() -> BoxFuture<'a, Output> + Send + Sync + 'a>;
pub struct Asyncify<F>(pub F);
macro_rules! impl_into_di {
($($generic:ident),*) => {
impl<Func, Output, Fut, $($generic),*> Injectable<Output, ($($generic,)*)> for Func
where
Func: Fn($($generic),*) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Output> + Send + 'static,
Output: 'static,
$($generic: Clone + Send + Sync + 'static),*
{
#[allow(non_snake_case)]
#[allow(unused_variables)]
fn inject<'a>(&'a self, container: &'a DependencyMap) -> CompiledFn<'a, Output> {
Arc::new(move || {
$(let $generic = std::borrow::Borrow::<$generic>::borrow(&container.get()).clone();)*
let fut = self( $( $generic ),* );
Box::pin(fut)
})
}
fn input_types() -> BTreeSet<Type> {
BTreeSet::from_iter(vec![
$(Type::of::<$generic>()),*
])
}
}
impl<Func, Output, $($generic),*> Injectable<Output, ($($generic,)*)> for Asyncify<Func>
where
Func: Fn($($generic),*) -> Output + Send + Sync + 'static,
Output: Send + 'static,
$($generic: Clone + Send + Sync + 'static),*
{
#[allow(non_snake_case)]
#[allow(unused_variables)]
fn inject<'a>(&'a self, container: &'a DependencyMap) -> CompiledFn<'a, Output> {
let Asyncify(this) = self;
Arc::new(move || {
$(let $generic = std::borrow::Borrow::<$generic>::borrow(&container.get()).clone();)*
let out = this( $( $generic ),* );
Box::pin(ready(out))
})
}
fn input_types() -> BTreeSet<Type> {
BTreeSet::from_iter(vec![
$(Type::of::<$generic>()),*
])
}
}
};
}
impl_into_di!();
impl_into_di!(T1);
impl_into_di!(T1, T2);
impl_into_di!(T1, T2, T3);
impl_into_di!(T1, T2, T3, T4);
impl_into_di!(T1, T2, T3, T4, T5);
impl_into_di!(T1, T2, T3, T4, T5, T6);
impl_into_di!(T1, T2, T3, T4, T5, T6, T7);
impl_into_di!(T1, T2, T3, T4, T5, T6, T7, T8);
impl_into_di!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
impl_into_di!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
impl_into_di!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl_into_di!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
#[macro_export]
macro_rules! deps {
($($dep:expr),*) => {
{
#[allow(unused_mut)]
let mut map = $crate::di::DependencyMap::new();
$(map.insert($dep);)*
map
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get() {
let mut map = DependencyMap::new();
map.insert(42i32);
map.insert("hello world");
map.insert_container(deps![true]);
assert_eq!(map.get(), Arc::new(42i32));
assert_eq!(map.get(), Arc::new("hello world"));
assert_eq!(map.get(), Arc::new(true));
}
#[test]
fn try_get() {
let mut map = DependencyMap::new();
assert_eq!(map.try_get::<i32>(), None);
map.insert(42i32);
assert_eq!(map.try_get(), Some(Arc::new(42i32)));
assert_eq!(map.try_get::<f32>(), None);
}
#[test]
fn same_keys() {
let mut map_bool1 = DependencyMap::new();
let mut map_bool2 = DependencyMap::new();
let map_empty = DependencyMap::new();
map_bool1.insert(false);
map_bool2.insert(true);
assert_eq!(map_bool1, map_bool2);
assert_ne!(map_bool1, map_empty);
assert_ne!(map_bool2, map_empty);
}
}