use crate::cache::LazyCache;
use crate::entry::{IServiceResolver, ServiceEntry, ServiceLifetime, ServiceStore};
use crate::error::RdiError;
use crate::provider::ServiceProvider;
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};
pub(crate) struct ValidationResolver<'a> {
store: &'a ServiceStore,
resolving: RwLock<Vec<usize>>,
stack: RwLock<Vec<(usize, ServiceLifetime)>>,
type_names: &'a HashMap<TypeId, &'static str>,
cache: &'a LazyCache,
provider: Arc<ServiceProvider>,
last_error: Mutex<Option<RdiError>>,
}
impl<'a> ValidationResolver<'a> {
pub(crate) fn new(
store: &'a ServiceStore,
type_names: &'a HashMap<TypeId, &'static str>,
cache: &'a LazyCache,
provider: Arc<ServiceProvider>,
) -> Self {
Self {
store,
resolving: RwLock::new(Vec::new()),
stack: RwLock::new(Vec::new()),
type_names,
cache,
provider,
last_error: Mutex::new(None),
}
}
pub(crate) fn take_error(&self) -> Option<RdiError> {
self.last_error.lock().unwrap().take()
}
fn get_type_name(&self, cache_key: usize) -> String {
self.store
.iter()
.find(|(_, entries)| entries.iter().any(|e| e.cache_key == cache_key))
.and_then(|(_, entries)| entries.first())
.map(|e| {
e.type_name
.split("::")
.last()
.unwrap_or(e.type_name)
.to_string()
})
.unwrap_or_else(|| format!("service {}", cache_key))
}
fn unkeyed_entry(&self, tid: TypeId) -> Option<&ServiceEntry> {
self.store.get(&tid)?.iter().rev().find(|e| e.key.is_none())
}
fn keyed_entry(&self, tid: TypeId, variant: &str) -> Option<&ServiceEntry> {
self.store
.get(&tid)?
.iter()
.rev()
.find(|e| e.key.as_deref() == Some(variant))
}
fn tid_by_name(&self, key: &str) -> Option<TypeId> {
self.type_names
.iter()
.find(|(_, name)| **name == key)
.map(|(tid, _)| *tid)
}
fn check_captive_dependency(&self, entry: &ServiceEntry) -> bool {
if entry.lifetime == ServiceLifetime::Scoped {
let stack = self.stack.read().unwrap();
if let Some(&(ancestor_ck, ServiceLifetime::Singleton)) = stack
.iter()
.find(|(_, lt)| *lt == ServiceLifetime::Singleton)
{
let singleton_name = self.get_type_name(ancestor_ck);
let scoped_name = self.get_type_name(entry.cache_key);
*self.last_error.lock().unwrap() = Some(RdiError::SingletonDependsOnScoped {
singleton: singleton_name,
scoped: scoped_name,
});
return true;
}
}
false
}
fn check_cycle(&self, entry: &ServiceEntry) -> bool {
let resolving = self.resolving.read().unwrap();
if resolving.contains(&entry.cache_key) {
let type_name = self.get_type_name(entry.cache_key);
let cycle_desc = if resolving.is_empty() {
type_name
} else {
let chain: Vec<String> =
resolving.iter().map(|&ck| self.get_type_name(ck)).collect();
format!("{} -> {}", chain.join(" -> "), type_name)
};
*self.last_error.lock().unwrap() = Some(RdiError::CircularDependency(cycle_desc));
return true;
}
false
}
fn resolve_via_factory(&self, entry: &ServiceEntry) -> Option<Arc<dyn Any + Send + Sync>> {
if self.check_captive_dependency(entry) {
return None;
}
if self.check_cycle(entry) {
return None;
}
self.resolving.write().unwrap().push(entry.cache_key);
self.stack
.write()
.unwrap()
.push((entry.cache_key, entry.lifetime));
let resolve_guard = match crate::provider::enter_resolve(entry.cache_key, entry.type_name) {
Ok(guard) => guard,
Err(err) => {
self.stack.write().unwrap().pop();
self.resolving.write().unwrap().pop();
*self.last_error.lock().unwrap() = Some(err);
return None;
}
};
let result = {
let _build_guard = crate::provider::BuildFactoryGuard::enter();
(entry.factory)(self as &dyn IServiceResolver)
};
drop(resolve_guard);
self.stack.write().unwrap().pop();
self.resolving.write().unwrap().pop();
if entry.lifetime == ServiceLifetime::Singleton {
self.cache.prefill(entry.cache_key, result.clone());
}
Some(result)
}
fn resolve_entry_cached(&self, entry: &ServiceEntry) -> Option<Arc<dyn Any + Send + Sync>> {
if let Some(cached) = self.cache.get(entry.cache_key) {
return Some(cached);
}
self.resolve_via_factory(entry)
}
pub(crate) fn validate_entry(
&self,
entry: &ServiceEntry,
) -> Option<Arc<dyn Any + Send + Sync>> {
self.resolve_entry_cached(entry)
}
}
impl IServiceResolver for ValidationResolver<'_> {
fn get_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
if let Some(tid) = self.tid_by_name(key) {
if let Some(entry) = self.unkeyed_entry(tid) {
if let Some(cached) = self.cache.get(entry.cache_key) {
return Some(cached);
}
return self.resolve_via_factory(entry);
}
}
if key == std::any::type_name::<dyn crate::scope::ScopeFactory>() {
return self.provider.builtin_scope_factory_any_cached();
}
if key == std::any::type_name::<ServiceProvider>() {
let provider: Arc<ServiceProvider> = Arc::clone(&self.provider);
return Some(Arc::new(provider) as Arc<dyn Any + Send + Sync>);
}
None
}
fn get_keyed_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>> {
let tid = self.tid_by_name(key)?;
let entry = self.keyed_entry(tid, variant)?;
self.resolve_entry_cached(entry)
}
fn get_all_any(&self, key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
let Some(tid) = self.tid_by_name(key) else {
return Vec::new();
};
let Some(entries) = self.store.get(&tid) else {
return Vec::new();
};
entries
.iter()
.filter(|e| e.key.is_none())
.filter_map(|entry| self.resolve_entry_cached(entry))
.collect()
}
fn get_all_keyed_any(&self, key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
let Some(tid) = self.tid_by_name(key) else {
return Vec::new();
};
let Some(entries) = self.store.get(&tid) else {
return Vec::new();
};
entries
.iter()
.filter(|e| e.key.is_some())
.filter_map(|entry| self.resolve_entry_cached(entry))
.collect()
}
fn get_all_any_result(&self, key: &str) -> Result<Vec<Arc<dyn Any + Send + Sync>>, RdiError> {
let mut out = Vec::new();
let Some(tid) = self.tid_by_name(key) else {
return Ok(out);
};
let Some(entries) = self.store.get(&tid) else {
return Ok(out);
};
for entry in entries.iter().filter(|e| e.key.is_none()) {
match self.resolve_entry_cached(entry) {
Some(v) => out.push(v),
None => {
return Err(self
.take_error()
.unwrap_or_else(|| RdiError::not_found(entry.type_name, None)))
}
}
}
Ok(out)
}
fn get_owned_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
let tid = self.tid_by_name(key)?;
let entry = self.unkeyed_entry(tid)?;
if entry.lifetime == ServiceLifetime::Singleton {
return None;
}
self.resolve_entry_cached(entry)
}
fn get_keyed_owned_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>> {
let tid = self.tid_by_name(key)?;
let entry = self.keyed_entry(tid, variant)?;
if entry.lifetime == ServiceLifetime::Singleton {
return None;
}
self.resolve_entry_cached(entry)
}
fn provider_arc(&self) -> Option<Arc<ServiceProvider>> {
Some(Arc::clone(&self.provider))
}
}