use crate::cache::{AnyService, LazyCache};
use crate::entry::{
AsyncServiceFactory, IServiceResolver, ServiceEntry, ServiceFactory, ServiceLifetime,
ServiceStore,
};
use crate::error::RdiError;
use crate::scope::ScopeFactory;
use crate::validation::ValidationResolver;
use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::collections::HashMap;
use std::panic::AssertUnwindSafe;
use std::sync::{Arc, Mutex, OnceLock, RwLock, Weak};
thread_local! {
static RESOLVING: RefCell<Vec<(usize, &'static str)>> = const { RefCell::new(Vec::new()) };
}
struct ResolveGuard;
impl Drop for ResolveGuard {
fn drop(&mut self) {
RESOLVING.with(|stack| {
stack.borrow_mut().pop();
});
}
}
fn enter_resolve(cache_key: usize, type_name: &'static str) -> Result<ResolveGuard, RdiError> {
RESOLVING.with(|stack| {
let mut s = stack.borrow_mut();
if s.iter().any(|(k, _)| *k == cache_key) {
let mut chain: Vec<&str> = s.iter().map(|(_, n)| *n).collect();
chain.push(type_name);
return Err(RdiError::CircularDependency(chain.join(" -> ")));
}
s.push((cache_key, type_name));
Ok(ResolveGuard)
})
}
pub struct ServiceProvider {
store: Arc<ServiceStore>,
type_map: Arc<HashMap<&'static str, TypeId>>,
singleton_cache: Arc<LazyCache>,
root_scoped_cache: LazyCache,
pub(crate) named: Arc<RwLock<HashMap<String, Arc<dyn Any + Send + Sync>>>>,
self_weak: OnceLock<Weak<ServiceProvider>>,
disposables: Mutex<Vec<Box<dyn FnOnce() + Send>>>,
}
impl ServiceProvider {
pub(crate) fn new(store: ServiceStore) -> Result<Arc<Self>, RdiError> {
if let Some(entry) = store
.values()
.flat_map(|entries| entries.iter())
.find(|e| e.async_factory.is_some())
{
return Err(RdiError::AsyncServiceRequiresBuildAsync {
service: entry.type_name.to_string(),
});
}
let mut type_map = HashMap::new();
for (&tid, entries) in &store {
if let Some(e) = entries.first() {
type_map.entry(e.type_name).or_insert(tid);
}
}
let singleton_cache = Arc::new(LazyCache::new());
Self::validate_dependencies(&store, &singleton_cache)?;
let sp_arc = Arc::new(Self {
store: Arc::new(store),
type_map: Arc::new(type_map),
singleton_cache,
root_scoped_cache: LazyCache::new(),
named: Arc::new(RwLock::new(HashMap::new())),
self_weak: OnceLock::new(),
disposables: Mutex::new(Vec::new()),
});
sp_arc
.self_weak
.get_or_init(|| Arc::downgrade(&sp_arc));
Self::init_sync_singletons(&sp_arc);
Ok(sp_arc)
}
fn init_sync_singletons(sp_arc: &Arc<Self>) {
let singleton_entries: Vec<(usize, ServiceFactory)> = sp_arc
.store
.values()
.flat_map(|entries| entries.iter())
.filter(|e| e.lifetime == ServiceLifetime::Singleton && e.async_factory.is_none())
.map(|e| (e.cache_key, e.factory.clone()))
.collect();
for (ck, factory) in &singleton_entries {
let resolver = sp_arc.as_ref() as &dyn IServiceResolver;
let factory = factory.clone();
sp_arc
.singleton_cache
.get_or_init_with(*ck, move || factory(resolver));
}
}
pub(crate) async fn new_async(
store: ServiceStore,
) -> Result<Arc<Self>, RdiError> {
let mut type_map = HashMap::new();
for (&tid, entries) in &store {
if let Some(e) = entries.first() {
type_map.entry(e.type_name).or_insert(tid);
}
}
let singleton_cache = Arc::new(LazyCache::new());
Self::validate_dependencies(&store, &singleton_cache)?;
let sp_arc = Arc::new(Self {
store: Arc::new(store),
type_map: Arc::new(type_map),
singleton_cache,
root_scoped_cache: LazyCache::new(),
named: Arc::new(RwLock::new(HashMap::new())),
self_weak: OnceLock::new(),
disposables: Mutex::new(Vec::new()),
});
sp_arc
.self_weak
.get_or_init(|| Arc::downgrade(&sp_arc));
Self::init_sync_singletons(&sp_arc);
let async_singletons: Vec<(usize, AsyncServiceFactory)> = sp_arc
.store
.values()
.flat_map(|entries| entries.iter())
.filter(|e| e.lifetime == ServiceLifetime::Singleton && e.async_factory.is_some())
.map(|e| (e.cache_key, e.async_factory.clone().unwrap()))
.collect();
for (ck, async_factory) in async_singletons {
let result = async_factory(Arc::clone(&sp_arc)).await;
sp_arc.singleton_cache.get_or_init_with(ck, || result);
}
Ok(sp_arc)
}
pub(crate) fn new_scope(parent: &Arc<Self>) -> Arc<Self> {
let sp = Self {
store: Arc::clone(&parent.store),
type_map: Arc::clone(&parent.type_map),
singleton_cache: Arc::clone(&parent.singleton_cache),
root_scoped_cache: LazyCache::new(),
named: Arc::clone(&parent.named),
self_weak: OnceLock::new(),
disposables: Mutex::new(Vec::new()),
};
let arc = Arc::new(sp);
let this: &Self = &arc;
this.self_weak.get_or_init(|| Arc::downgrade(&arc));
arc
}
pub fn register_disposable(&self, cleanup: Box<dyn FnOnce() + Send>) {
self.disposables.lock().unwrap().push(cleanup);
}
pub(crate) fn dispose_scoped(&self) {
let to_run: Vec<Box<dyn FnOnce() + Send>> = {
let mut guard = self.disposables.lock().unwrap();
std::mem::take(&mut *guard)
};
for cleanup in to_run {
cleanup();
}
}
fn validate_dependencies(store: &ServiceStore, cache: &LazyCache) -> Result<(), RdiError> {
let type_names: HashMap<TypeId, &'static str> = store
.iter()
.map(|(&tid, entries)| {
let name = entries.first().map(|e| e.type_name).unwrap_or("<unknown>");
(tid, name)
})
.collect();
for entries in store.values() {
for entry in entries {
if entry.lifetime == ServiceLifetime::Singleton && entry.async_factory.is_none() {
let resolver = ValidationResolver::new(store, &type_names, cache);
let type_name = entry.type_name;
let key = entry.key.clone();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
match &key {
Some(k) => resolver.get_keyed_any(type_name, k),
None => resolver.get_any(type_name),
}
}));
if let Some(err) = resolver.take_error() {
return Err(err);
}
match result {
Ok(_) => {}
Err(panic_info) => {
return Err(RdiError::from_panic_payload(type_name, panic_info));
}
}
}
}
}
Ok(())
}
pub fn get<T: ?Sized + Send + Sync + 'static>(&self) -> Result<Arc<T>, RdiError> {
let type_name = std::any::type_name::<T>();
match self.resolve_optional::<T>() {
Ok(Some(v)) => Ok(v),
Ok(None) => Err(RdiError::not_found(type_name, None)),
Err(e) => Err(e),
}
}
pub fn get_optional<T: ?Sized + Send + Sync + 'static>(&self) -> Option<Arc<T>> {
self.resolve_optional::<T>().ok().flatten()
}
pub async fn get_async<T: ?Sized + Send + Sync + 'static>(
&self,
) -> Result<Arc<T>, RdiError> {
let type_name = std::any::type_name::<T>();
let tid = TypeId::of::<T>();
let entry = self
.store
.get(&tid)
.and_then(|entries| entries.iter().find(|e| e.key.is_none()))
.ok_or_else(|| RdiError::not_found(type_name, None))?;
if entry.lifetime == ServiceLifetime::Singleton {
return self.get::<T>();
}
match self.resolve_async_any(entry, type_name).await? {
Some(arc_any) => Self::extract(arc_any).ok_or_else(|| RdiError::not_found(type_name, None)),
None => self.get::<T>(),
}
}
pub async fn get_keyed_async<T: ?Sized + Send + Sync + 'static>(
&self,
key: &str,
) -> Result<Arc<T>, RdiError> {
let type_name = std::any::type_name::<T>();
let tid = TypeId::of::<T>();
let entry = self
.store
.get(&tid)
.and_then(|entries| entries.iter().find(|e| e.key.as_deref() == Some(key)))
.ok_or_else(|| RdiError::keyed_not_found(type_name, key, None))?;
if entry.lifetime == ServiceLifetime::Singleton {
return self.get_keyed::<T>(key);
}
match self.resolve_async_any(entry, type_name).await? {
Some(arc_any) => {
Self::extract(arc_any).ok_or_else(|| RdiError::keyed_not_found(type_name, key, None))
}
None => self.get_keyed::<T>(key),
}
}
async fn resolve_async_any(
&self,
entry: &ServiceEntry,
type_name: &'static str,
) -> Result<Option<AnyService>, RdiError> {
match entry.lifetime {
ServiceLifetime::Scoped => {
let async_factory = entry
.async_factory
.clone()
.ok_or_else(|| RdiError::not_found(type_name, None))?;
let provider_arc = self.provider_arc().ok_or_else(|| RdiError::FactoryPanic {
service: type_name.to_string(),
payload: "provider_arc() returned None for async resolution".into(),
})?;
self.root_scoped_cache
.get_or_init_with_async(entry.cache_key, || {
Self::run_async_factory_safely(type_name, async_factory, provider_arc)
})
.await
.map(Some)
}
ServiceLifetime::Transient => {
if let Some(async_factory) = entry.async_factory.clone() {
let provider_arc = self.provider_arc().ok_or_else(|| RdiError::FactoryPanic {
service: type_name.to_string(),
payload: "provider_arc() returned None for async resolution".into(),
})?;
Self::run_async_factory_safely(type_name, async_factory, provider_arc)
.await
.map(Some)
} else {
Ok(None)
}
}
_ => Ok(None),
}
}
async fn run_async_factory_safely(
type_name: &'static str,
async_factory: AsyncServiceFactory,
provider_arc: Arc<ServiceProvider>,
) -> Result<AnyService, RdiError> {
let fut = async_factory(provider_arc);
match futures::FutureExt::catch_unwind(AssertUnwindSafe(fut)).await {
Ok(arc_any) => Ok(arc_any),
Err(payload) => Err(RdiError::from_panic_payload(type_name, payload)),
}
}
pub fn get_keyed<T: ?Sized + Send + Sync + 'static>(&self, key: &str) -> Result<Arc<T>, RdiError> {
let type_name = std::any::type_name::<T>();
match self.resolve_keyed_optional::<T>(key) {
Ok(Some(v)) => Ok(v),
Ok(None) => Err(RdiError::keyed_not_found(type_name, key, None)),
Err(e) => Err(e),
}
}
pub fn get_owned<T: Send + Sync + 'static>(&self) -> Result<T, RdiError> {
let type_name = std::any::type_name::<T>();
let tid = TypeId::of::<T>();
let entry = self
.store
.get(&tid)
.and_then(|entries| entries.iter().find(|e| e.key.is_none()))
.ok_or_else(|| RdiError::not_found(type_name, None))?;
if entry.lifetime == ServiceLifetime::Singleton {
return Err(RdiError::OwnedNotSupported(type_name));
}
let arc_any = match std::panic::catch_unwind(AssertUnwindSafe(|| {
self.create_owned_any_by_entry(entry)
})) {
Ok(Some(v)) => v,
Ok(None) => return Err(RdiError::not_found(type_name, None)),
Err(payload) => return Err(RdiError::from_panic_payload(type_name, payload)),
};
let arc_t = Self::extract(arc_any).ok_or_else(|| RdiError::not_found(type_name, None))?;
Arc::try_unwrap(arc_t).map_err(|_| RdiError::OwnedNotSupported(type_name))
}
pub fn try_get_owned<T: Send + Sync + 'static>(&self) -> Option<T> {
self.get_owned().ok()
}
pub fn get_keyed_owned<T: Send + Sync + 'static>(&self, key: &str) -> Result<T, RdiError> {
let type_name = std::any::type_name::<T>();
let tid = TypeId::of::<T>();
let entry = self
.store
.get(&tid)
.and_then(|entries| entries.iter().find(|e| e.key.as_deref() == Some(key)))
.ok_or_else(|| RdiError::keyed_not_found(type_name, key, None))?;
if entry.lifetime == ServiceLifetime::Singleton {
return Err(RdiError::OwnedNotSupported(type_name));
}
let arc_any = match std::panic::catch_unwind(AssertUnwindSafe(|| {
self.create_owned_any_by_entry(entry)
})) {
Ok(Some(v)) => v,
Ok(None) => return Err(RdiError::keyed_not_found(type_name, key, None)),
Err(payload) => return Err(RdiError::from_panic_payload(type_name, payload)),
};
let arc_t =
Self::extract(arc_any).ok_or_else(|| RdiError::keyed_not_found(type_name, key, None))?;
Arc::try_unwrap(arc_t).map_err(|_| RdiError::OwnedNotSupported(type_name))
}
pub fn try_get_keyed_owned<T: Send + Sync + 'static>(&self, key: &str) -> Option<T> {
self.get_keyed_owned(key).ok()
}
pub fn get_all<T: ?Sized + Send + Sync + 'static>(&self) -> Vec<Arc<T>> {
let tid = TypeId::of::<T>();
match self.store.get(&tid) {
Some(entries) => entries
.iter()
.filter_map(|e| {
let arc = self.get_any_by_entry(e)?;
Self::extract(arc)
})
.collect(),
None => Vec::new(),
}
}
pub fn scope(self: &Arc<Self>) -> crate::scope::Scope {
crate::scope::Scope::new(self.clone())
}
#[allow(dead_code)]
fn try_get<T: ?Sized + Send + Sync + 'static>(&self) -> Option<Arc<T>> {
self.resolve_optional::<T>().ok().flatten()
}
#[allow(dead_code)]
fn try_get_keyed<T: ?Sized + Send + Sync + 'static>(&self, key: &str) -> Option<Arc<T>> {
self.resolve_keyed_optional::<T>(key).ok().flatten()
}
fn resolve_optional<T: ?Sized + Send + Sync + 'static>(
&self,
) -> Result<Option<Arc<T>>, RdiError> {
let tid = TypeId::of::<T>();
if let Some(entry) = self
.store
.get(&tid)
.and_then(|entries| entries.iter().find(|e| e.key.is_none()))
{
let arc = self.resolve_entry_catching(entry)?;
return Ok(Self::extract(arc));
}
if tid == TypeId::of::<dyn ScopeFactory>() {
let Some(p) = self.provider_arc() else {
return Ok(None);
};
let sf: Arc<dyn ScopeFactory> = p;
return Ok(Self::extract(Arc::new(sf) as AnyService));
}
Ok(None)
}
fn resolve_keyed_optional<T: ?Sized + Send + Sync + 'static>(
&self,
key: &str,
) -> Result<Option<Arc<T>>, RdiError> {
let tid = TypeId::of::<T>();
let Some(entry) = self
.store
.get(&tid)
.and_then(|entries| entries.iter().find(|e| e.key.as_deref() == Some(key)))
else {
return Ok(None);
};
let arc = self.resolve_entry_catching(entry)?;
Ok(Self::extract(arc))
}
pub(crate) fn resolve_entry_catching(&self, entry: &ServiceEntry) -> Result<AnyService, RdiError> {
match std::panic::catch_unwind(AssertUnwindSafe(|| self.get_any_by_entry(entry))) {
Ok(Some(v)) => Ok(v),
Ok(None) => Err(RdiError::not_found(entry.type_name, None)),
Err(payload) => Err(RdiError::from_panic_payload(entry.type_name, payload)),
}
}
pub(crate) fn get_any_by_entry(&self, entry: &ServiceEntry) -> Option<AnyService> {
let _guard = match enter_resolve(entry.cache_key, entry.type_name) {
Ok(g) => g,
Err(e) => std::panic::panic_any(e),
};
match entry.lifetime {
ServiceLifetime::Singleton => {
let factory = entry.factory.clone();
let resolver = self as &dyn IServiceResolver;
Some(self.singleton_cache.get_or_init_with(entry.cache_key, move || {
factory(resolver)
}))
}
ServiceLifetime::Scoped => {
let factory = entry.factory.clone();
let resolver = self as &dyn IServiceResolver;
Some(self.root_scoped_cache.get_or_init_with(entry.cache_key, move || {
factory(resolver)
}))
}
ServiceLifetime::Transient => Some((entry.factory)(self)),
}
}
pub(crate) fn create_owned_any_by_entry(&self, entry: &ServiceEntry) -> Option<AnyService> {
match entry.lifetime {
ServiceLifetime::Singleton => None,
ServiceLifetime::Scoped | ServiceLifetime::Transient => {
let _guard = match enter_resolve(entry.cache_key, entry.type_name) {
Ok(g) => g,
Err(e) => std::panic::panic_any(e),
};
Some((entry.factory)(self as &dyn IServiceResolver))
}
}
}
pub(crate) fn extract<T: ?Sized + Send + Sync + 'static>(
arc: Arc<dyn Any + Send + Sync>,
) -> Option<Arc<T>> {
let double: Arc<Arc<T>> = arc.downcast::<Arc<T>>().ok()?;
Some(Arc::clone(&*double))
}
pub(crate) fn entry_by_str(&self, type_name: &str, variant: &str) -> Option<&ServiceEntry> {
let tid = self.type_map.get(type_name)?;
self.store
.get(tid)?
.iter()
.find(|e| e.key.as_deref() == Some(variant))
}
pub fn get_named<T: Send + Sync + 'static>(&self, name: &str) -> Option<Arc<T>> {
self.named
.read()
.unwrap()
.get(name)?
.clone()
.downcast::<T>()
.ok()
}
pub fn get_named_any(&self, name: &str) -> Option<Arc<dyn Any + Send + Sync>> {
self.named.read().unwrap().get(name).cloned()
}
pub fn register_named<T: Send + Sync + 'static>(&self, name: &str, service: Arc<T>) {
self.named
.write()
.unwrap()
.insert(name.to_string(), service);
}
pub fn remove_named(&self, name: &str) {
self.named.write().unwrap().remove(name);
}
pub fn rdi_register_named(&self, name: &str, service: Arc<dyn Any + Send + Sync>) {
self.named
.write()
.unwrap()
.insert(name.to_string(), service);
}
pub fn rdi_remove_named(&self, name: &str) {
self.named.write().unwrap().remove(name);
}
}
impl ScopeFactory for ServiceProvider {
fn create_scope(&self) -> crate::scope::Scope {
self.self_weak
.get()
.and_then(|w| w.upgrade())
.map(crate::scope::Scope::new)
.expect("ScopeFactory::create_scope: ServiceProvider self_weak not initialized")
}
}
impl IServiceResolver for ServiceProvider {
fn get_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
let tid = self.type_map.get(key)?;
let entry = self.store.get(tid)?.iter().find(|e| e.key.is_none())?;
self.get_any_by_entry(entry)
}
fn get_keyed_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>> {
let entry = self.entry_by_str(key, variant)?;
self.get_any_by_entry(entry)
}
fn get_by_type_id(&self, tid: TypeId) -> Option<Arc<dyn Any + Send + Sync>> {
if let Some(entry) = self.store.get(&tid)?.iter().find(|e| e.key.is_none()) {
return self.get_any_by_entry(entry);
}
if tid == TypeId::of::<dyn ScopeFactory>() {
let p = self.provider_arc()?;
let sf: Arc<dyn ScopeFactory> = p;
return Some(Arc::new(sf) as AnyService);
}
None
}
fn get_keyed_by_type_id(
&self,
tid: TypeId,
key: &str,
) -> Option<Arc<dyn Any + Send + Sync>> {
let entry = self
.store
.get(&tid)?
.iter()
.find(|e| e.key.as_deref() == Some(key))?;
self.get_any_by_entry(entry)
}
fn get_all_any(&self, key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
let tid = match self.type_map.get(key) {
Some(t) => t,
None => return Vec::new(),
};
match self.store.get(tid) {
Some(entries) => entries
.iter()
.filter_map(|e| self.get_any_by_entry(e))
.collect(),
None => Vec::new(),
}
}
fn get_owned_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
let tid = self.type_map.get(key)?;
let entry = self.store.get(tid)?.iter().find(|e| e.key.is_none())?;
self.create_owned_any_by_entry(entry)
}
fn get_keyed_owned_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>> {
let entry = self.entry_by_str(key, variant)?;
self.create_owned_any_by_entry(entry)
}
fn provider_arc(&self) -> Option<Arc<ServiceProvider>> {
self.self_weak.get().and_then(|w| w.upgrade())
}
}
#[cfg(test)]
mod tests {
use crate::collection::ServiceCollection;
use std::sync::Arc;
#[derive(Debug, PartialEq)]
struct Calc(i32);
#[test]
fn optional_missing() {
let p = ServiceCollection::new().build().unwrap();
assert!(p.get_optional::<i32>().is_none());
}
#[test]
fn all_basic() {
let p = ServiceCollection::new()
.keyed_singleton("a", |_| Arc::new(Calc(1)))
.keyed_singleton("b", |_| Arc::new(Calc(2)))
.build()
.unwrap();
assert_eq!(p.get_all::<Calc>().len(), 2);
}
}