Skip to main content

hosting_di/
scope.rs

1use crate::container::{Container, Slots};
2use crate::sync::ConstructionLock;
3use crate::{ResolveError, Resolver};
4use std::fmt;
5use std::sync::{Arc, OnceLock};
6
7/// A unit of work with its own scoped instances, such as one request.
8///
9/// Owns a handle to the container, so it can be moved into other threads or
10/// tasks and keeps working after the provider that created it is dropped.
11pub struct Scope {
12    // Declared first: scoped instances must be released before the container,
13    // which may be dropped with this scope.
14    state: ScopeState,
15    container: Arc<Container>,
16}
17
18#[derive(Default)]
19pub(crate) struct ScopeState {
20    slots: OnceLock<Slots>,
21    pub(crate) construction: ConstructionLock,
22}
23
24impl Scope {
25    pub(crate) fn new(container: Arc<Container>) -> Self {
26        Self {
27            state: ScopeState::default(),
28            container,
29        }
30    }
31
32    /// Resolves a service of any lifetime.
33    ///
34    /// When `T` is registered more than once, the last registration wins; use
35    /// [`resolve_all`](Self::resolve_all) for every one.
36    pub fn resolve<T: ?Sized + Send + Sync + 'static>(&self) -> Result<Arc<T>, ResolveError> {
37        Resolver::scoped(&self.container, &self.state).resolve()
38    }
39
40    /// Resolves every registration of `T`, in registration order.
41    pub fn resolve_all<T: ?Sized + Send + Sync + 'static>(
42        &self,
43    ) -> Result<Vec<Arc<T>>, ResolveError> {
44        Resolver::scoped(&self.container, &self.state).resolve_all()
45    }
46}
47
48impl fmt::Debug for Scope {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        f.debug_struct("Scope").finish_non_exhaustive()
51    }
52}
53
54impl ScopeState {
55    /// Storage is allocated on the first scoped resolution, so scopes that only
56    /// touch singletons and transients never allocate.
57    pub(crate) fn slots(&self, container: &Container) -> &Slots {
58        self.slots
59            .get_or_init(|| Slots::new(container.scoped_count))
60    }
61}