use crate::collection::{Registration, Source};
use crate::container::{Container, Erased, Slots};
use crate::key::Key;
use crate::scope::ScopeState;
use crate::sync::ConstructionLock;
use crate::{BoxError, Lifetime, ResolveError};
use std::cell::RefCell;
use std::fmt;
use std::ptr;
use std::sync::Arc;
pub struct Resolver<'a> {
container: &'a Container,
scope: Option<&'a ScopeState>,
parent: Option<&'a Resolver<'a>>,
service: Option<&'a Registration>,
}
impl fmt::Debug for Resolver<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Resolver")
.field("constructing", &self.path())
.finish_non_exhaustive()
}
}
impl<'a> Resolver<'a> {
pub(crate) fn root(container: &'a Container) -> Self {
Self {
container,
scope: None,
parent: None,
service: None,
}
}
pub(crate) fn scoped(container: &'a Container, scope: &'a ScopeState) -> Self {
Self {
container,
scope: Some(scope),
parent: None,
service: None,
}
}
pub fn resolve<T: ?Sized + Send + Sync + 'static>(&self) -> Result<Arc<T>, ResolveError> {
let key = Key::of::<T>();
let Some(registration) = self.container.registration(key) else {
return Err(ResolveError::NotRegistered {
path: self.path_to(key),
});
};
let instance = self.instance(registration)?;
self.cast(registration, instance)
}
pub fn resolve_all<T: ?Sized + Send + Sync + 'static>(
&self,
) -> Result<Vec<Arc<T>>, ResolveError> {
let registrations = self.container.registrations_of(Key::of::<T>());
let mut all = Vec::with_capacity(registrations.len());
for registration in registrations {
let instance = self.instance(registration)?;
all.push(self.cast(registration, instance)?);
}
Ok(all)
}
pub fn construction_error(&self, source: impl Into<BoxError>) -> ResolveError {
ResolveError::Construction {
path: self.path(),
source: source.into(),
}
}
pub(crate) fn failure(&self, error: BoxError) -> ResolveError {
match error.downcast::<ResolveError>() {
Ok(error) => *error,
Err(error) => self.construction_error(error),
}
}
fn instance(&self, registration: &Registration) -> Result<Erased, ResolveError> {
match registration.lifetime {
Lifetime::Singleton => self.singleton(registration),
Lifetime::Scoped => self.scoped_instance(registration),
Lifetime::Transient => self.construct(registration, self.scope),
}
}
fn cast<T: ?Sized + 'static>(
&self,
registration: &Registration,
instance: Erased,
) -> Result<Arc<T>, ResolveError> {
registration
.cast
.downcast_ref::<fn(Erased) -> Option<Arc<T>>>()
.and_then(|cast| cast(instance))
.ok_or_else(|| ResolveError::NotRegistered {
path: self.path_to(registration.key),
})
}
fn singleton(&self, registration: &Registration) -> Result<Erased, ResolveError> {
let container = self.container;
self.cached(
registration,
&container.singletons,
&container.construction,
None,
)
}
fn scoped_instance(&self, registration: &Registration) -> Result<Erased, ResolveError> {
let Some(scope) = self.scope else {
let mut path = self.path();
path.push(registration.implementation);
return Err(ResolveError::ScopedFromRoot { path });
};
self.cached(
registration,
scope.slots(self.container),
&scope.construction,
Some(scope),
)
}
fn cached(
&self,
registration: &Registration,
slots: &Slots,
lock: &ConstructionLock,
scope: Option<&ScopeState>,
) -> Result<Erased, ResolveError> {
if let Some(instance) = slots.get(registration.slot) {
return Ok(instance);
}
let _guard = lock.lock();
if let Some(instance) = slots.get(registration.slot) {
return Ok(instance);
}
let instance = self.construct(registration, scope)?;
slots.fill(registration.slot, Arc::clone(&instance));
Ok(instance)
}
fn construct(
&self,
registration: &'a Registration,
scope: Option<&'a ScopeState>,
) -> Result<Erased, ResolveError> {
let _constructing =
Constructing::enter(registration).map_err(|path| ResolveError::Cycle { path })?;
let frame = Resolver {
container: self.container,
scope,
parent: Some(self),
service: Some(registration),
};
let result = match ®istration.source {
Source::Construct(construct) => construct(&frame),
Source::Instance(instance) => Ok(Arc::clone(instance)),
};
#[cfg(feature = "logging")]
match &result {
Ok(_) => tracing::debug!(
service = registration.implementation,
lifetime = ?registration.lifetime,
"constructed service"
),
Err(error) => tracing::debug!(
service = registration.implementation,
lifetime = ?registration.lifetime,
%error,
"service construction failed"
),
}
result
}
fn path(&self) -> Vec<&'static str> {
let mut path = Vec::new();
let mut frame = Some(self);
while let Some(current) = frame {
if let Some(service) = current.service {
path.push(service.implementation);
}
frame = current.parent;
}
path.reverse();
path
}
fn path_to(&self, key: Key) -> Vec<&'static str> {
let mut path = self.path();
path.push(key.name);
path
}
}
thread_local! {
static CONSTRUCTING: RefCell<Vec<(*const Registration, &'static str)>> =
const { RefCell::new(Vec::new()) };
}
struct Constructing;
impl Constructing {
fn enter(registration: &Registration) -> Result<Self, Vec<&'static str>> {
let this: *const Registration = registration;
CONSTRUCTING.with_borrow_mut(|stack| {
if let Some(start) = stack.iter().position(|&(entry, _)| ptr::eq(entry, this)) {
let mut path: Vec<_> = stack[start..].iter().map(|&(_, name)| name).collect();
path.push(registration.implementation);
return Err(path);
}
stack.push((this, registration.implementation));
Ok(Constructing)
})
}
}
impl Drop for Constructing {
fn drop(&mut self) {
CONSTRUCTING.with_borrow_mut(|stack| {
stack.pop();
});
}
}