use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::collections::HashSet;
use std::fmt;
use std::sync::Arc;
thread_local! {
static RESOLVING_STACK: RefCell<HashSet<InjectionKey>> = RefCell::new(HashSet::new());
}
pub(crate) struct ResolutionGuard {
key: InjectionKey,
}
impl ResolutionGuard {
pub(crate) fn new(key: InjectionKey) -> Self {
RESOLVING_STACK.with(|stack| {
let mut stack = stack.borrow_mut();
if !stack.insert(key.clone()) {
panic!(
"Circular dependency detected while resolving service: {:?}",
key
);
}
});
Self { key }
}
}
impl Drop for ResolutionGuard {
fn drop(&mut self) {
RESOLVING_STACK.with(|stack| {
stack.borrow_mut().remove(&self.key);
});
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub(crate) struct InjectionKey {
pub(crate) type_id: TypeId,
pub(crate) name: Option<String>,
}
impl InjectionKey {
pub(crate) fn new<T: ?Sized + Any>() -> Self {
Self {
type_id: TypeId::of::<T>(),
name: None,
}
}
pub(crate) fn new_with_name<T: ?Sized + Any>(name: &str) -> Self {
Self {
type_id: TypeId::of::<T>(),
name: Some(name.to_owned()),
}
}
}
impl fmt::Debug for InjectionKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.name {
Some(name) => write!(f, "Key(TypeId({:?}), Name({}))", self.type_id, name),
None => write!(f, "Key(TypeId({:?}))", self.type_id),
}
}
}
pub(crate) enum Provider {
Singleton {
cell: once_cell::sync::OnceCell<Box<dyn Any + Send + Sync>>,
factory: Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>,
},
Transient {
factory: Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>,
},
}
impl Provider {
pub(crate) fn get_singleton_ref(&self) -> Option<&Box<dyn Any + Send + Sync>> {
match self {
Provider::Singleton { cell, factory } => Some(cell.get_or_init(factory)),
_ => None,
}
}
pub(crate) fn get_transient_owned(&self) -> Option<Box<dyn Any + Send + Sync>> {
match self {
Provider::Transient { factory } => Some(factory()),
_ => None,
}
}
}