use super::*;
impl Injector {
pub(crate) fn store_named_provider<T>(
&self,
name: &str,
provider: Provider<T>,
) -> Result<(), Error>
where
T: ?Sized + Send + Sync + 'static,
{
let key = NamedTypeKey::of::<T>(name);
let type_name = std::any::type_name::<T>();
let scope = provider.scope;
let scope_text = scope.to_string();
let graph_meta = ProviderGraphMeta::of::<T>(scope, provider.dependency_hints.clone());
#[cfg(feature = "tracing")]
debug!(
type_name = type_name,
name = %name,
scope = %scope,
op = "provider_store_named",
"Storing named provider definition"
);
#[cfg(feature = "lock-free")]
{
match self.inner.named_providers.entry(key.clone()) {
DashEntry::Occupied(_) => {
#[cfg(feature = "tracing")]
debug!(
type_name = type_name,
name = %name,
scope = %scope,
op = "provider_store_named",
"Named provider registration rejected: duplicate binding"
);
return Err(Error::provider_already_registered_named(
type_name,
name,
scope_text.as_str(),
));
}
DashEntry::Vacant(entry) => {
entry.insert(Shared::new(provider));
}
}
self.inner.graph_named_providers.insert(key, graph_meta);
}
#[cfg(not(feature = "lock-free"))]
{
let mut providers = self.inner.named_providers.write().unwrap();
if providers.contains_key(&key) {
#[cfg(feature = "tracing")]
debug!(
type_name = type_name,
name = %name,
scope = %scope,
op = "provider_store_named",
"Named provider registration rejected: duplicate binding"
);
return Err(Error::provider_already_registered_named(
type_name,
name,
scope_text.as_str(),
));
}
providers.insert(key.clone(), Shared::new(provider));
drop(providers);
self.inner
.graph_named_providers
.write()
.unwrap()
.insert(key, graph_meta);
}
#[cfg(feature = "tracing")]
debug!(
type_name = type_name,
name = %name,
scope = %scope,
op = "provider_store_named",
"Named provider definition stored"
);
Ok(())
}
pub(crate) fn replace_provider<T>(&self, provider: Provider<T>) -> Result<(), Error>
where
T: ?Sized + Send + Sync + 'static,
{
let type_id = TypeId::of::<T>();
let type_name = std::any::type_name::<T>();
#[cfg(feature = "tracing")]
let scope = provider.scope;
let graph_meta =
ProviderGraphMeta::of::<T>(provider.scope, provider.dependency_hints.clone());
#[cfg(feature = "tracing")]
debug!(
type_name = type_name,
scope = %scope,
op = "provider_replace",
"Replacing provider definition"
);
#[cfg(feature = "lock-free")]
{
match self.inner.providers.entry(type_id) {
DashEntry::Occupied(mut entry) => {
entry.insert(Shared::new(provider));
}
DashEntry::Vacant(_) => {
#[cfg(feature = "tracing")]
debug!(
type_name = type_name,
scope = %scope,
op = "provider_replace",
"Provider replace rejected: no previous binding"
);
return Err(Error::service_not_provided_for_override(type_name));
}
}
self.inner.graph_providers.insert(type_id, graph_meta);
}
#[cfg(not(feature = "lock-free"))]
{
let mut providers = self.inner.providers.write().unwrap();
if !providers.contains_key(&type_id) {
#[cfg(feature = "tracing")]
debug!(
type_name = type_name,
scope = %scope,
op = "provider_replace",
"Provider replace rejected: no previous binding"
);
return Err(Error::service_not_provided_for_override(type_name));
}
providers.insert(type_id, Shared::new(provider));
drop(providers);
self.inner
.graph_providers
.write()
.unwrap()
.insert(type_id, graph_meta);
}
self.clear_instance_cache::<T>();
#[cfg(feature = "tracing")]
debug!(
type_name = type_name,
scope = %scope,
op = "provider_replace",
"Provider definition replaced and cache invalidated"
);
Ok(())
}
pub(crate) fn clear_instance_cache<T>(&self)
where
T: ?Sized + Send + Sync + 'static,
{
let type_id = TypeId::of::<T>();
#[cfg(feature = "tracing")]
let type_name = std::any::type_name::<T>();
#[cfg(feature = "lock-free")]
{
self.inner.instances.remove(&type_id);
}
#[cfg(not(feature = "lock-free"))]
{
self.inner.instances.write().unwrap().remove(&type_id);
}
#[cfg(feature = "tracing")]
trace!(
type_name = type_name,
op = "instance_cache_clear",
"Cleared cached instance for type"
);
}
pub(crate) fn get_instance<T>(&self) -> Option<Shared<Instance<T>>>
where
T: ?Sized + Send + Sync + 'static,
{
let type_id = TypeId::of::<T>();
#[cfg(feature = "tracing")]
let type_name = std::any::type_name::<T>();
#[cfg(feature = "lock-free")]
let local = self
.inner
.instances
.get(&type_id)
.map(|value| value.value().clone());
#[cfg(not(feature = "lock-free"))]
let local = self.inner.instances.read().unwrap().get(&type_id).cloned();
if local.is_some() {
#[cfg(feature = "tracing")]
trace!(
type_name = type_name,
op = "instance_lookup",
source = "local",
hit = true,
"Instance cache hit"
);
return local.and_then(|instance| instance.downcast::<Instance<T>>().ok());
}
#[cfg(feature = "tracing")]
trace!(
type_name = type_name,
op = "instance_lookup",
source = "local",
hit = false,
has_parent = self.inner.parent.is_some(),
"Instance cache miss"
);
if let Some(parent) = &self.inner.parent {
let parent_injector = Injector {
inner: parent.clone(),
};
#[cfg(feature = "tracing")]
trace!(
type_name = type_name,
op = "instance_lookup",
source = "parent",
"Falling back to parent cache"
);
return parent_injector.get_instance::<T>();
}
#[cfg(feature = "tracing")]
trace!(
type_name = type_name,
op = "instance_lookup",
source = "none",
"Instance not cached in injector hierarchy"
);
None
}
pub(crate) fn get_set_instance<T>(
&self,
provider_ref: &Shared<Provider<T>>,
) -> Option<Shared<Instance<T>>>
where
T: ?Sized + Send + Sync + 'static,
{
let key = SetProviderKey::of::<T>(provider_ref);
#[cfg(feature = "tracing")]
let type_name = std::any::type_name::<T>();
#[cfg(feature = "lock-free")]
let local = self
.inner
.set_instances
.get(&key)
.map(|value| value.value().clone());
#[cfg(not(feature = "lock-free"))]
let local = self.inner.set_instances.read().unwrap().get(&key).cloned();
#[cfg(feature = "tracing")]
trace!(
type_name = type_name,
op = "instance_lookup_set",
provider_ptr = key.provider_ptr,
hit = local.is_some(),
"Set-binding instance lookup completed"
);
local.and_then(|instance| instance.downcast::<Instance<T>>().ok())
}
pub(crate) fn get_instance_named<T>(&self, name: &str) -> Option<Shared<Instance<T>>>
where
T: ?Sized + Send + Sync + 'static,
{
let key = NamedTypeKey::of::<T>(name);
#[cfg(feature = "tracing")]
let type_name = std::any::type_name::<T>();
#[cfg(feature = "lock-free")]
let local = self
.inner
.named_instances
.get(&key)
.map(|value| value.value().clone());
#[cfg(not(feature = "lock-free"))]
let local = self
.inner
.named_instances
.read()
.unwrap()
.get(&key)
.cloned();
if local.is_some() {
#[cfg(feature = "tracing")]
trace!(
type_name = type_name,
name = %name,
op = "instance_lookup_named",
source = "local",
hit = true,
"Named instance cache hit"
);
return local.and_then(|instance| instance.downcast::<Instance<T>>().ok());
}
#[cfg(feature = "tracing")]
trace!(
type_name = type_name,
name = %name,
op = "instance_lookup_named",
source = "local",
hit = false,
has_parent = self.inner.parent.is_some(),
"Named instance cache miss"
);
if let Some(parent) = &self.inner.parent {
let parent_injector = Injector {
inner: parent.clone(),
};
#[cfg(feature = "tracing")]
trace!(
type_name = type_name,
name = %name,
op = "instance_lookup_named",
source = "parent",
"Falling back to parent cache for named instance"
);
return parent_injector.get_instance_named::<T>(name);
}
#[cfg(feature = "tracing")]
trace!(
type_name = type_name,
name = %name,
op = "instance_lookup_named",
source = "none",
"Named instance not cached in injector hierarchy"
);
None
}
}