Skip to main content

nidus_core/container/
mod.rs

1//! Typed dependency container primitives.
2
3mod dependency;
4mod request_scope;
5
6use std::{
7    any::{Any, TypeId, type_name},
8    collections::HashMap,
9    hash::{BuildHasherDefault, Hasher},
10    sync::Arc,
11};
12
13use crate::{NidusError, ProviderEntry, ProviderLifetime, Result};
14
15pub use dependency::{Factory, Inject, Lazy, Optional, Scoped};
16pub use request_scope::{RequestScope, SharedRequestScope};
17
18/// Hash map keyed by `TypeId` using the identity hasher below.
19pub(crate) type TypeIdMap<V> = HashMap<TypeId, V, BuildHasherDefault<TypeIdHasher>>;
20
21/// Identity hasher for `TypeId` keys.
22///
23/// `TypeId`'s `Hash` impl feeds an already high-quality compiler-generated
24/// 64-bit hash through `write_u64`, so mixing it again through the default
25/// SipHash only adds cost. Store that value directly, mirroring the map used
26/// by `http::Extensions`.
27#[derive(Default)]
28pub(crate) struct TypeIdHasher(u64);
29
30impl Hasher for TypeIdHasher {
31    fn write(&mut self, bytes: &[u8]) {
32        // TypeId only calls write_u64; keep a correct fallback for any other
33        // key shape rather than assuming the std implementation never changes.
34        for &byte in bytes {
35            self.0 = self.0.rotate_left(8) ^ u64::from(byte);
36        }
37    }
38
39    fn write_u64(&mut self, value: u64) {
40        self.0 = value;
41    }
42
43    fn finish(&self) -> u64 {
44        self.0
45    }
46}
47
48/// Type-indexed dependency container.
49#[derive(Default)]
50pub struct Container {
51    providers: TypeIdMap<ProviderEntry>,
52}
53
54impl Container {
55    /// Creates an empty container.
56    pub fn new() -> Self {
57        Self::default()
58    }
59
60    /// Creates a request scope for request-lifetime providers.
61    pub fn request_scope(&self) -> RequestScope<'_> {
62        RequestScope::borrowed(self)
63    }
64
65    /// Registers a concrete singleton value.
66    pub fn register_singleton<T>(&mut self, value: T) -> Result<()>
67    where
68        T: Send + Sync + 'static,
69    {
70        let value = Arc::new(value);
71        self.insert::<T>(ProviderLifetime::Singleton, move |_container| {
72            Ok(Arc::clone(&value) as Arc<dyn Any + Send + Sync>)
73        })
74    }
75
76    /// Replaces a singleton provider, intended for explicit test overrides.
77    pub fn override_singleton<T>(&mut self, value: T) -> Result<()>
78    where
79        T: Send + Sync + 'static,
80    {
81        self.providers.remove(&TypeId::of::<T>());
82        self.register_singleton(value)
83    }
84
85    /// Registers a provider factory.
86    pub fn register_factory<T, F>(&mut self, lifetime: ProviderLifetime, factory: F) -> Result<()>
87    where
88        T: Send + Sync + 'static,
89        F: Fn(&Container) -> Result<T> + Send + Sync + 'static,
90    {
91        self.insert::<T>(lifetime, move |container| {
92            factory(container).map(|value| Arc::new(value) as Arc<dyn Any + Send + Sync>)
93        })
94    }
95
96    /// Registers a singleton provider factory.
97    pub fn register_singleton_factory<T, F>(&mut self, factory: F) -> Result<()>
98    where
99        T: Send + Sync + 'static,
100        F: Fn(&Container) -> Result<T> + Send + Sync + 'static,
101    {
102        self.register_factory::<T, F>(ProviderLifetime::Singleton, factory)
103    }
104
105    /// Registers a transient provider factory.
106    pub fn register_transient<T, F>(&mut self, factory: F) -> Result<()>
107    where
108        T: Send + Sync + 'static,
109        F: Fn(&Container) -> Result<T> + Send + Sync + 'static,
110    {
111        self.register_factory::<T, F>(ProviderLifetime::Transient, factory)
112    }
113
114    /// Registers a request-lifetime provider factory.
115    pub fn register_request<T, F>(&mut self, factory: F) -> Result<()>
116    where
117        T: Send + Sync + 'static,
118        F: Fn(&Container) -> Result<T> + Send + Sync + 'static,
119    {
120        self.register_factory::<T, F>(ProviderLifetime::Request, factory)
121    }
122
123    /// Registers a request-lifetime provider factory that resolves dependencies
124    /// through the active request scope.
125    pub fn register_request_scoped<T, F>(&mut self, factory: F) -> Result<()>
126    where
127        T: Send + Sync + 'static,
128        F: for<'scope> Fn(&RequestScope<'scope>) -> Result<T> + Send + Sync + 'static,
129    {
130        self.insert_request_scoped::<T>(
131            |_container| {
132                Err(NidusError::RequestScopeRequired {
133                    type_name: type_name::<T>(),
134                })
135            },
136            move |scope| factory(scope).map(|value| Arc::new(value) as Arc<dyn Any + Send + Sync>),
137        )
138    }
139
140    /// Resolves a typed dependency reference.
141    pub fn inject<T>(&self) -> Result<Inject<T>>
142    where
143        T: Send + Sync + 'static,
144    {
145        self.resolve::<T>().map(Inject::new)
146    }
147
148    /// Resolves an optional typed dependency reference.
149    ///
150    /// Missing providers become `Optional::new(None)`, while registered providers
151    /// that fail to construct still return their original error.
152    pub fn optional<T>(&self) -> Result<Optional<T>>
153    where
154        T: Send + Sync + 'static,
155    {
156        match self.inject::<T>() {
157            Ok(value) => Ok(Optional::new(Some(value))),
158            Err(NidusError::MissingProvider { .. }) => Ok(Optional::new(None)),
159            Err(error) => Err(error),
160        }
161    }
162
163    /// Resolves a shared typed dependency.
164    pub fn resolve<T>(&self) -> Result<Arc<T>>
165    where
166        T: Send + Sync + 'static,
167    {
168        let entry = self.entry::<T>()?;
169        if entry.lifetime() == ProviderLifetime::Request {
170            return Err(NidusError::RequestScopeRequired {
171                type_name: type_name::<T>(),
172            });
173        }
174        let erased = entry.resolve_erased(self)?;
175        downcast::<T>(erased)
176    }
177
178    /// Eagerly constructs every registered singleton provider and caches it.
179    ///
180    /// Singletons are otherwise constructed lazily on first resolution, which
181    /// uses a blocking `Condvar` wait when two callers race to construct the
182    /// same provider. Calling this at startup pre-constructs each singleton so
183    /// later resolutions (including from async request handlers) hit the cached
184    /// value and never reach that wait, avoiding an async-runtime worker
185    /// stalling on first use. Transient and request-lifetime providers are
186    /// skipped.
187    ///
188    /// A singleton whose factory errors or panics will do so here, failing
189    /// startup fast instead of on first request.
190    pub fn eagerly_resolve_singletons(&self) -> Result<()> {
191        for entry in self.providers.values() {
192            if entry.lifetime() == ProviderLifetime::Singleton {
193                entry.resolve_erased(self)?;
194            }
195        }
196        Ok(())
197    }
198
199    fn insert<T>(
200        &mut self,
201        lifetime: ProviderLifetime,
202        factory: impl Fn(&Container) -> Result<Arc<dyn Any + Send + Sync>> + Send + Sync + 'static,
203    ) -> Result<()>
204    where
205        T: Send + Sync + 'static,
206    {
207        let type_id = TypeId::of::<T>();
208        if self.providers.contains_key(&type_id) {
209            return Err(NidusError::DuplicateProvider {
210                type_name: type_name::<T>(),
211            });
212        }
213
214        self.providers.insert(
215            type_id,
216            ProviderEntry::new(type_id, type_name::<T>(), lifetime, Arc::new(factory)),
217        );
218        Ok(())
219    }
220
221    fn insert_request_scoped<T>(
222        &mut self,
223        factory: impl Fn(&Container) -> Result<Arc<dyn Any + Send + Sync>> + Send + Sync + 'static,
224        request_factory: impl for<'scope> Fn(
225            &RequestScope<'scope>,
226        ) -> Result<Arc<dyn Any + Send + Sync>>
227        + Send
228        + Sync
229        + 'static,
230    ) -> Result<()>
231    where
232        T: Send + Sync + 'static,
233    {
234        let type_id = TypeId::of::<T>();
235        if self.providers.contains_key(&type_id) {
236            return Err(NidusError::DuplicateProvider {
237                type_name: type_name::<T>(),
238            });
239        }
240
241        self.providers.insert(
242            type_id,
243            ProviderEntry::new_request_scoped(
244                type_id,
245                type_name::<T>(),
246                Arc::new(factory),
247                Arc::new(request_factory),
248            ),
249        );
250        Ok(())
251    }
252
253    fn entry<T>(&self) -> Result<&ProviderEntry>
254    where
255        T: Send + Sync + 'static,
256    {
257        self.providers
258            .get(&TypeId::of::<T>())
259            .ok_or_else(|| NidusError::MissingProvider {
260                type_name: type_name::<T>(),
261            })
262    }
263}
264
265fn downcast<T>(erased: Arc<dyn Any + Send + Sync>) -> Result<Arc<T>>
266where
267    T: Send + Sync + 'static,
268{
269    erased
270        .downcast::<T>()
271        .map_err(|_| NidusError::MissingProvider {
272            type_name: type_name::<T>(),
273        })
274}
275
276#[cfg(test)]
277mod tests {
278    use std::{
279        any::TypeId,
280        hash::{Hash, Hasher},
281    };
282
283    use super::TypeIdHasher;
284
285    #[test]
286    fn type_id_hasher_uses_written_u64_directly() {
287        let mut hasher = TypeIdHasher::default();
288        hasher.write_u64(42);
289        assert_eq!(hasher.finish(), 42);
290    }
291
292    #[test]
293    fn type_id_hasher_distinguishes_type_ids() {
294        let mut first = TypeIdHasher::default();
295        TypeId::of::<u32>().hash(&mut first);
296        let mut second = TypeIdHasher::default();
297        TypeId::of::<u64>().hash(&mut second);
298        assert_ne!(first.finish(), second.finish());
299    }
300
301    #[test]
302    fn type_id_hasher_byte_fallback_mixes_all_bytes() {
303        let mut first = TypeIdHasher::default();
304        first.write(b"ab");
305        let mut second = TypeIdHasher::default();
306        second.write(b"ba");
307        assert_ne!(first.finish(), second.finish());
308    }
309}