use crate::core::contracts::{DIBuilder, GetInput};
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum Lifetime {
#[default]
Transient,
Singleton,
}
#[derive(Default)]
pub struct TypeMap(HashMap<TypeId, (Lifetime, Box<dyn Any>)>);
impl TypeMap {
pub fn set<T>(&mut self, t: T, lifetime: Option<Lifetime>)
where
T: Any + 'static,
{
self.0.insert(
TypeId::of::<T>(),
(lifetime.unwrap_or_default(), Box::new(t)),
);
}
pub fn get<T>(&self) -> Option<&T>
where
T: Any + 'static,
{
self.0
.get(&TypeId::of::<T>())
.map(|(_, boxed)| boxed.downcast_ref::<T>().unwrap())
}
pub fn get_mut<T>(&mut self) -> Option<&mut T>
where
T: Any + 'static,
{
self.0
.get_mut(&TypeId::of::<T>())
.map(|(_, boxed)| boxed.downcast_mut::<T>().unwrap())
}
pub fn get_lifetime<T>(&self) -> Option<Lifetime>
where
T: Any + 'static,
{
self.0
.get(&TypeId::of::<T>())
.map(|(lifetime, _)| *lifetime)
}
pub fn has<T>(&self) -> bool
where
T: Any + 'static,
{
self.0.contains_key(&TypeId::of::<T>())
}
}
#[derive(Clone)]
pub struct DIObj<T: Clone>(Arc<Mutex<T>>);
impl<T: Clone> DIObj<T> {
pub fn new(t: T) -> Self {
Self(Arc::new(Mutex::new(t)))
}
pub fn extract(&self) -> T {
self.0.lock().unwrap().clone()
}
}
#[derive(Default)]
pub struct DIManager(TypeMap);
impl DIManager {
pub async fn build<T>(&mut self) -> Option<DIObj<T::Output>>
where
T: DIBuilder,
{
let input = T::Input::get_input(self)?;
let obj = T::build(input).await;
let sync_obj = DIObj::new(obj);
self.0
.set::<DIObj<T::Output>>(sync_obj.clone(), Some(Lifetime::Transient));
Some(sync_obj)
}
pub async fn register<T>(&mut self, lifetime: Option<Lifetime>) -> &mut Self
where
T: DIBuilder,
{
let input = T::Input::get_input(self)
.expect("Some input dependencies are missing. Please register them beforehand.");
let obj = T::build(input).await;
let sync_obj = DIObj::new(obj);
self.0.set::<DIObj<T::Output>>(sync_obj.clone(), lifetime);
self
}
pub async fn resolve<T>(&mut self) -> Option<DIObj<T::Output>>
where
T: DIBuilder,
{
match self.0.get_lifetime::<DIObj<T::Output>>() {
Some(Lifetime::Transient) => self.build::<T>().await,
Some(Lifetime::Singleton) => {
let obj = self.0.get::<DIObj<T::Output>>().unwrap().extract();
let sync_obj = DIObj::new(obj);
Some(sync_obj)
}
None => None,
}
}
pub fn has<T>(&self) -> bool
where
T: Any + 'static,
{
self.0.has::<T>()
}
}
impl<T: Clone + 'static> GetInput for DIObj<T> {
fn get_input(manager: &DIManager) -> Option<Self> {
manager.0.get::<Self>().cloned()
}
}
impl GetInput for () {
fn get_input(_: &DIManager) -> Option<Self> {
Some(())
}
}
impl<S, T> GetInput for (S, T)
where
S: GetInput,
T: GetInput,
{
fn get_input(manager: &DIManager) -> Option<Self> {
S::get_input(manager).and_then(|s| T::get_input(manager).map(|t| (s, t)))
}
}