Skip to main content

hosting_di/
resolver.rs

1use crate::collection::{Registration, Source};
2use crate::container::{Container, Erased, Slots};
3use crate::key::Key;
4use crate::scope::ScopeState;
5use crate::sync::ConstructionLock;
6use crate::{BoxError, Lifetime, ResolveError};
7use std::cell::RefCell;
8use std::fmt;
9use std::ptr;
10use std::sync::Arc;
11
12/// Resolves dependencies while a service is being constructed.
13///
14/// Each construction gets its own frame linked to the frame that requested it,
15/// so the chain of services under construction lives on the stack.
16///
17/// Use it only on the constructing thread. Construction of cached instances is
18/// serialised per container and per scope, so handing a resolver to another
19/// thread and waiting for it deadlocks.
20pub struct Resolver<'a> {
21    container: &'a Container,
22    scope: Option<&'a ScopeState>,
23    parent: Option<&'a Resolver<'a>>,
24    service: Option<&'a Registration>,
25}
26
27impl fmt::Debug for Resolver<'_> {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        f.debug_struct("Resolver")
30            .field("constructing", &self.path())
31            .finish_non_exhaustive()
32    }
33}
34
35impl<'a> Resolver<'a> {
36    pub(crate) fn root(container: &'a Container) -> Self {
37        Self {
38            container,
39            scope: None,
40            parent: None,
41            service: None,
42        }
43    }
44
45    pub(crate) fn scoped(container: &'a Container, scope: &'a ScopeState) -> Self {
46        Self {
47            container,
48            scope: Some(scope),
49            parent: None,
50            service: None,
51        }
52    }
53
54    /// Resolves a dependency of the service under construction.
55    ///
56    /// When `T` is registered more than once, the last registration wins; use
57    /// [`resolve_all`](Self::resolve_all) for every one.
58    pub fn resolve<T: ?Sized + Send + Sync + 'static>(&self) -> Result<Arc<T>, ResolveError> {
59        let key = Key::of::<T>();
60        let Some(registration) = self.container.registration(key) else {
61            return Err(ResolveError::NotRegistered {
62                path: self.path_to(key),
63            });
64        };
65
66        let instance = self.instance(registration)?;
67        self.cast(registration, instance)
68    }
69
70    /// Resolves every registration of `T`, in registration order. The first
71    /// failure fails the whole call; nothing is cached for the failed member.
72    pub fn resolve_all<T: ?Sized + Send + Sync + 'static>(
73        &self,
74    ) -> Result<Vec<Arc<T>>, ResolveError> {
75        let registrations = self.container.registrations_of(Key::of::<T>());
76        let mut all = Vec::with_capacity(registrations.len());
77        for registration in registrations {
78            let instance = self.instance(registration)?;
79            all.push(self.cast(registration, instance)?);
80        }
81        Ok(all)
82    }
83
84    /// Wraps a constructor failure with the current dependency path.
85    pub fn construction_error(&self, source: impl Into<BoxError>) -> ResolveError {
86        ResolveError::Construction {
87            path: self.path(),
88            source: source.into(),
89        }
90    }
91
92    /// Like [`construction_error`](Self::construction_error), but passes a
93    /// dependency's `ResolveError` through so its path is kept.
94    pub(crate) fn failure(&self, error: BoxError) -> ResolveError {
95        match error.downcast::<ResolveError>() {
96            Ok(error) => *error,
97            Err(error) => self.construction_error(error),
98        }
99    }
100
101    fn instance(&self, registration: &Registration) -> Result<Erased, ResolveError> {
102        match registration.lifetime {
103            Lifetime::Singleton => self.singleton(registration),
104            Lifetime::Scoped => self.scoped_instance(registration),
105            Lifetime::Transient => self.construct(registration, self.scope),
106        }
107    }
108
109    fn cast<T: ?Sized + 'static>(
110        &self,
111        registration: &Registration,
112        instance: Erased,
113    ) -> Result<Arc<T>, ResolveError> {
114        registration
115            .cast
116            .downcast_ref::<fn(Erased) -> Option<Arc<T>>>()
117            .and_then(|cast| cast(instance))
118            .ok_or_else(|| ResolveError::NotRegistered {
119                path: self.path_to(registration.key),
120            })
121    }
122
123    fn singleton(&self, registration: &Registration) -> Result<Erased, ResolveError> {
124        let container = self.container;
125        self.cached(
126            registration,
127            &container.singletons,
128            &container.construction,
129            None,
130        )
131    }
132
133    fn scoped_instance(&self, registration: &Registration) -> Result<Erased, ResolveError> {
134        let Some(scope) = self.scope else {
135            // Name the implementation: when resolving a set, the service type
136            // alone doesn't say which member is scoped.
137            let mut path = self.path();
138            path.push(registration.implementation);
139            return Err(ResolveError::ScopedFromRoot { path });
140        };
141        self.cached(
142            registration,
143            scope.slots(self.container),
144            &scope.construction,
145            Some(scope),
146        )
147    }
148
149    /// Returns the cached instance, constructing it under `lock` on first use.
150    /// Failed constructions store nothing, so the next resolution retries.
151    fn cached(
152        &self,
153        registration: &Registration,
154        slots: &Slots,
155        lock: &ConstructionLock,
156        scope: Option<&ScopeState>,
157    ) -> Result<Erased, ResolveError> {
158        if let Some(instance) = slots.get(registration.slot) {
159            return Ok(instance);
160        }
161
162        let _guard = lock.lock();
163        if let Some(instance) = slots.get(registration.slot) {
164            return Ok(instance);
165        }
166        let instance = self.construct(registration, scope)?;
167        slots.fill(registration.slot, Arc::clone(&instance));
168        Ok(instance)
169    }
170
171    fn construct(
172        &self,
173        registration: &'a Registration,
174        scope: Option<&'a ScopeState>,
175    ) -> Result<Erased, ResolveError> {
176        let _constructing =
177            Constructing::enter(registration).map_err(|path| ResolveError::Cycle { path })?;
178        let frame = Resolver {
179            container: self.container,
180            scope,
181            parent: Some(self),
182            service: Some(registration),
183        };
184        let result = match &registration.source {
185            Source::Construct(construct) => construct(&frame),
186            Source::Instance(instance) => Ok(Arc::clone(instance)),
187        };
188
189        #[cfg(feature = "logging")]
190        match &result {
191            Ok(_) => tracing::debug!(
192                service = registration.implementation,
193                lifetime = ?registration.lifetime,
194                "constructed service"
195            ),
196            Err(error) => tracing::debug!(
197                service = registration.implementation,
198                lifetime = ?registration.lifetime,
199                %error,
200                "service construction failed"
201            ),
202        }
203
204        result
205    }
206
207    /// Services under construction, outermost first.
208    fn path(&self) -> Vec<&'static str> {
209        let mut path = Vec::new();
210        let mut frame = Some(self);
211        while let Some(current) = frame {
212            if let Some(service) = current.service {
213                path.push(service.implementation);
214            }
215            frame = current.parent;
216        }
217        path.reverse();
218        path
219    }
220
221    fn path_to(&self, key: Key) -> Vec<&'static str> {
222        let mut path = self.path();
223        path.push(key.name);
224        path
225    }
226}
227
228thread_local! {
229    /// Registrations under construction on this thread. Kept outside the frame
230    /// chain so a resolution that starts a new chain on the same thread, such
231    /// as a factory resolving through a captured provider, still sees what it
232    /// is nested in. A registration belongs to exactly one container, so its
233    /// address identifies it.
234    static CONSTRUCTING: RefCell<Vec<(*const Registration, &'static str)>> =
235        const { RefCell::new(Vec::new()) };
236}
237
238/// Marks a registration as under construction on this thread until dropped.
239struct Constructing;
240
241impl Constructing {
242    /// Fails with the cycle path if `registration` is already under
243    /// construction on this thread.
244    fn enter(registration: &Registration) -> Result<Self, Vec<&'static str>> {
245        let this: *const Registration = registration;
246        CONSTRUCTING.with_borrow_mut(|stack| {
247            if let Some(start) = stack.iter().position(|&(entry, _)| ptr::eq(entry, this)) {
248                let mut path: Vec<_> = stack[start..].iter().map(|&(_, name)| name).collect();
249                path.push(registration.implementation);
250                return Err(path);
251            }
252            stack.push((this, registration.implementation));
253            Ok(Constructing)
254        })
255    }
256}
257
258impl Drop for Constructing {
259    fn drop(&mut self) {
260        CONSTRUCTING.with_borrow_mut(|stack| {
261            stack.pop();
262        });
263    }
264}