1use crate::{DiResult, Injectable, InjectionContext};
8use async_trait::async_trait;
9use dashmap::DashMap;
10use std::any::{Any, TypeId};
11use std::future::Future;
12use std::marker::PhantomData;
13use std::sync::{Arc, OnceLock};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17pub enum DependencyScope {
18 #[default]
20 Singleton,
21 Request,
23 Transient,
25}
26
27impl DependencyScope {
28 pub fn outlives(&self, dependent: DependencyScope) -> bool {
36 !matches!(
37 (dependent, *self),
38 (DependencyScope::Singleton, DependencyScope::Request)
39 )
40 }
41}
42
43#[async_trait]
48pub trait FactoryTrait: Send + Sync {
49 async fn create(&self, ctx: &InjectionContext) -> DiResult<Arc<dyn Any + Send + Sync>>;
51}
52
53pub struct AsyncFactory<F, Fut, T>
55where
56 F: Fn(Arc<InjectionContext>) -> Fut + Send + Sync,
57 Fut: Future<Output = DiResult<T>> + Send,
58 T: Any + Send + Sync + 'static,
59{
60 factory: F,
61 _phantom: std::marker::PhantomData<fn() -> (Fut, T)>,
62}
63
64impl<F, Fut, T> AsyncFactory<F, Fut, T>
65where
66 F: Fn(Arc<InjectionContext>) -> Fut + Send + Sync,
67 Fut: Future<Output = DiResult<T>> + Send,
68 T: Any + Send + Sync + 'static,
69{
70 pub fn new(factory: F) -> Self {
72 Self {
73 factory,
74 _phantom: std::marker::PhantomData,
75 }
76 }
77}
78
79#[async_trait]
80impl<F, Fut, T> FactoryTrait for AsyncFactory<F, Fut, T>
81where
82 F: Fn(Arc<InjectionContext>) -> Fut + Send + Sync,
83 Fut: Future<Output = DiResult<T>> + Send + 'static,
84 T: Any + Send + Sync + 'static,
85{
86 async fn create(&self, ctx: &InjectionContext) -> DiResult<Arc<dyn Any + Send + Sync>> {
87 let ctx_arc = Arc::new(ctx.clone());
88 let instance = (self.factory)(ctx_arc).await?;
89 Ok(Arc::new(instance))
90 }
91}
92
93pub struct InjectableFactory<T>(PhantomData<T>);
99
100impl<T> Default for InjectableFactory<T> {
101 fn default() -> Self {
102 Self(PhantomData)
103 }
104}
105
106impl<T> InjectableFactory<T> {
107 pub fn new() -> Self {
109 Self::default()
110 }
111}
112
113#[async_trait]
114impl<T: Injectable + Any + Send + Sync + 'static> FactoryTrait for InjectableFactory<T> {
115 async fn create(&self, ctx: &InjectionContext) -> DiResult<Arc<dyn Any + Send + Sync>> {
116 let ctx_arc = Arc::new(ctx.clone());
119 let resolve_ctx = crate::resolve_context::ResolveContext {
120 root: crate::resolve_context::RESOLVE_CTX
121 .try_with(|outer| Arc::clone(&outer.root))
122 .unwrap_or_else(|_| Arc::clone(&ctx_arc)),
123 current: Arc::clone(&ctx_arc),
124 };
125
126 let value = crate::resolve_context::RESOLVE_CTX
127 .scope(resolve_ctx, T::inject(ctx))
128 .await?;
129 Ok(Arc::new(value))
130 }
131}
132
133type BoxedFactory = Box<dyn FactoryTrait>;
135
136pub struct DependencyRegistry {
141 factories: DashMap<TypeId, BoxedFactory>,
142 scopes: DashMap<TypeId, DependencyScope>,
143 dependencies: DashMap<TypeId, Vec<TypeId>>,
145 type_names: DashMap<TypeId, &'static str>,
147 qualified_type_names: DashMap<TypeId, &'static str>,
150}
151
152impl DependencyRegistry {
153 pub fn new() -> Self {
155 Self {
156 factories: DashMap::new(),
157 scopes: DashMap::new(),
158 dependencies: DashMap::new(),
159 type_names: DashMap::new(),
160 qualified_type_names: DashMap::new(),
161 }
162 }
163
164 pub fn register<T: Any + Send + Sync + 'static>(
178 &self,
179 scope: DependencyScope,
180 factory: impl FactoryTrait + 'static,
181 ) {
182 let type_id = TypeId::of::<T>();
183 let type_name = std::any::type_name::<T>();
184 if self.factories.contains_key(&type_id) {
189 let short = type_name.rsplit("::").next().unwrap_or(type_name);
190 panic!(
191 "Duplicate DependencyRegistry registration for type `{type_name}`.\n\
192\n\
193Hint: reinhardt DI uses TypeId as the sole registry key. Two factories\n\
194returning the same type will conflict regardless of function name or scope.\n\
195Use a distinct newtype (e.g., `struct Primary{short}({short})`) for each."
196 );
197 }
198 self.factories.insert(type_id, Box::new(factory));
199 self.scopes.insert(type_id, scope);
200 }
201
202 pub fn register_async<T, F, Fut>(&self, scope: DependencyScope, factory: F)
204 where
205 T: Any + Send + Sync + 'static,
206 F: Fn(Arc<InjectionContext>) -> Fut + Send + Sync + 'static,
207 Fut: Future<Output = DiResult<T>> + Send + 'static,
208 {
209 self.register::<T>(scope, AsyncFactory::new(factory));
210 }
211
212 pub fn get_scope<T: Any + 'static>(&self) -> Option<DependencyScope> {
214 let type_id = TypeId::of::<T>();
215 self.scopes.get(&type_id).map(|entry| *entry.value())
216 }
217
218 pub fn is_registered<T: Any + 'static>(&self) -> bool {
220 let type_id = TypeId::of::<T>();
221 self.factories.contains_key(&type_id)
222 }
223
224 pub fn len(&self) -> usize {
226 self.factories.len()
227 }
228
229 pub fn is_empty(&self) -> bool {
231 self.factories.is_empty()
232 }
233
234 pub async fn create<T: Any + Send + Sync + 'static>(
236 &self,
237 ctx: &InjectionContext,
238 ) -> DiResult<Arc<T>> {
239 let type_id = TypeId::of::<T>();
240
241 let factory = self.factories.get(&type_id).ok_or_else(|| {
242 crate::DiError::DependencyNotRegistered {
243 type_name: std::any::type_name::<T>().to_string(),
244 }
245 })?;
246
247 let any_arc = factory.create(ctx).await?;
248
249 any_arc
250 .downcast::<T>()
251 .map_err(|_| crate::DiError::Internal {
252 message: format!(
253 "Failed to downcast dependency: expected {}, got different type",
254 std::any::type_name::<T>()
255 ),
256 })
257 }
258
259 pub fn get_dependencies(&self, type_id: TypeId) -> Vec<TypeId> {
263 self.dependencies
264 .get(&type_id)
265 .map(|deps| deps.value().clone())
266 .unwrap_or_default()
267 }
268
269 pub fn get_all_dependencies(&self) -> std::collections::HashMap<TypeId, Vec<TypeId>> {
273 self.dependencies
274 .iter()
275 .map(|entry| (*entry.key(), entry.value().clone()))
276 .collect()
277 }
278
279 pub fn get_type_names(&self) -> std::collections::HashMap<TypeId, &'static str> {
283 self.type_names
284 .iter()
285 .map(|entry| (*entry.key(), *entry.value()))
286 .collect()
287 }
288
289 #[doc(hidden)]
294 pub fn register_dependencies(&self, type_id: TypeId, deps: impl AsRef<[TypeId]>) {
295 self.dependencies.insert(type_id, deps.as_ref().to_vec());
296 }
297
298 #[doc(hidden)]
303 pub fn register_type_name(&self, type_id: TypeId, type_name: &'static str) {
304 self.type_names.insert(type_id, type_name);
305 }
306
307 pub(crate) fn is_registered_by_id(&self, type_id: TypeId) -> bool {
309 self.factories.contains_key(&type_id)
310 }
311
312 pub(crate) fn get_scope_by_id(&self, type_id: TypeId) -> Option<DependencyScope> {
314 self.scopes.get(&type_id).map(|entry| *entry.value())
315 }
316
317 pub(crate) fn get_type_name(&self, type_id: TypeId) -> Option<&'static str> {
319 self.type_names.get(&type_id).map(|entry| *entry.value())
320 }
321
322 #[doc(hidden)]
326 pub fn register_qualified_type_name(&self, type_id: TypeId, qualified_name: &'static str) {
327 self.qualified_type_names.insert(type_id, qualified_name);
328 }
329
330 pub fn get_qualified_type_name(&self, type_id: &TypeId) -> Option<&'static str> {
332 self.qualified_type_names.get(type_id).map(|r| *r.value())
333 }
334
335 pub fn iter_qualified_type_names(&self) -> impl Iterator<Item = (TypeId, &'static str)> + '_ {
337 self.qualified_type_names
338 .iter()
339 .map(|entry| (*entry.key(), *entry.value()))
340 }
341}
342
343#[cfg(feature = "testing")]
344impl DependencyRegistry {
345 pub fn register_override<T, F, Fut>(
384 self: &std::sync::Arc<Self>,
385 scope: crate::registry::DependencyScope,
386 factory: F,
387 ) -> crate::testing::OverrideGuard
388 where
389 T: std::any::Any + Send + Sync + 'static,
390 F: Fn(std::sync::Arc<crate::InjectionContext>) -> Fut + Send + Sync + 'static,
391 Fut: std::future::Future<Output = crate::DiResult<T>> + Send + 'static,
392 {
393 let type_id = std::any::TypeId::of::<T>();
394 let async_factory = crate::registry::AsyncFactory::new(factory);
395 let boxed: Box<dyn crate::registry::FactoryTrait> = Box::new(async_factory);
396
397 let previous_factory = self.factories.insert(type_id, boxed);
399 let previous_scope = self.scopes.insert(type_id, scope);
400
401 debug_assert!(
407 previous_factory.is_some() == previous_scope.is_some(),
408 "torn override state: factories/scopes diverged for `{}`",
409 std::any::type_name::<T>()
410 );
411 let previous = match (previous_factory, previous_scope) {
412 (Some(f), Some(s)) => Some((f, s)),
413 _ => None,
414 };
415
416 crate::testing::OverrideGuard {
417 type_id,
418 previous,
419 registry: std::sync::Arc::downgrade(self),
420 }
421 }
422
423 pub(crate) fn restore_override(
431 &self,
432 type_id: std::any::TypeId,
433 factory: Box<dyn crate::registry::FactoryTrait>,
434 scope: crate::registry::DependencyScope,
435 ) {
436 self.factories.insert(type_id, factory);
437 self.scopes.insert(type_id, scope);
438 }
439
440 pub(crate) fn remove_override(&self, type_id: std::any::TypeId) {
444 self.factories.remove(&type_id);
445 self.scopes.remove(&type_id);
446 }
447}
448
449impl Default for DependencyRegistry {
450 fn default() -> Self {
451 Self::new()
452 }
453}
454
455static GLOBAL_REGISTRY: OnceLock<Arc<DependencyRegistry>> = OnceLock::new();
457
458pub fn global_registry() -> &'static Arc<DependencyRegistry> {
460 GLOBAL_REGISTRY.get_or_init(|| {
461 let registry = Arc::new(DependencyRegistry::new());
462 initialize_registry(®istry);
463 registry
464 })
465}
466
467#[cfg(test)]
478pub fn reset_global_registry() {
479 unsafe {
483 let ptr = std::ptr::addr_of!(GLOBAL_REGISTRY) as *mut OnceLock<Arc<DependencyRegistry>>;
484 std::ptr::write(ptr, OnceLock::new());
485 }
486}
487
488pub struct DependencyRegistration {
490 pub type_id: TypeId,
492 pub type_name: &'static str,
494 pub scope: DependencyScope,
496 pub dependencies: &'static [TypeId],
498 pub register_fn: fn(&DependencyRegistry),
500}
501
502impl DependencyRegistration {
503 pub const fn new<T: Send + Sync + 'static>(
505 type_name: &'static str,
506 scope: DependencyScope,
507 register_fn: fn(&DependencyRegistry),
508 ) -> Self {
509 Self {
510 type_id: TypeId::of::<T>(),
511 type_name,
512 scope,
513 dependencies: &[],
514 register_fn,
515 }
516 }
517
518 pub const fn new_with_deps<T: Send + Sync + 'static>(
520 type_name: &'static str,
521 scope: DependencyScope,
522 dependencies: &'static [TypeId],
523 register_fn: fn(&DependencyRegistry),
524 ) -> Self {
525 Self {
526 type_id: TypeId::of::<T>(),
527 type_name,
528 scope,
529 dependencies,
530 register_fn,
531 }
532 }
533}
534
535inventory::collect!(DependencyRegistration);
537
538pub struct InjectableRegistration {
544 pub register_fn: fn(&DependencyRegistry),
546}
547
548impl InjectableRegistration {
549 pub const fn new(register_fn: fn(&DependencyRegistry)) -> Self {
551 Self { register_fn }
552 }
553}
554
555inventory::collect!(InjectableRegistration);
556
557fn initialize_registry(registry: &DependencyRegistry) {
559 for registration in inventory::iter::<DependencyRegistration> {
560 (registration.register_fn)(registry);
561 }
562 for registration in inventory::iter::<InjectableRegistration> {
563 (registration.register_fn)(registry);
564 }
565}
566
567#[macro_export]
571macro_rules! submit_registration {
572 ($registration:expr) => {
573 $crate::inventory::submit! {
574 $registration
575 }
576 };
577}
578
579#[cfg(test)]
580mod tests {
581 use super::*;
582 use crate::scope::SingletonScope;
583 use rstest::*;
584
585 #[derive(Clone)]
586 struct TestService {
587 value: i32,
588 }
589
590 #[rstest]
591 #[tokio::test]
592 async fn test_registry_basic() {
593 let registry = DependencyRegistry::new();
594
595 registry.register_async::<TestService, _, _>(DependencyScope::Singleton, |_ctx| async {
596 Ok(TestService { value: 42 })
597 });
598
599 assert!(registry.is_registered::<TestService>());
600 assert_eq!(
601 registry.get_scope::<TestService>(),
602 Some(DependencyScope::Singleton)
603 );
604
605 let singleton_scope = Arc::new(SingletonScope::new());
606 let ctx = InjectionContext::builder(singleton_scope).build();
607
608 let service = registry.create::<TestService>(&ctx).await.unwrap();
609 assert_eq!(service.value, 42);
610 }
611
612 #[rstest]
613 #[tokio::test]
614 async fn test_registry_not_registered() {
615 let registry = DependencyRegistry::new();
616 let singleton_scope = Arc::new(SingletonScope::new());
617 let ctx = InjectionContext::builder(singleton_scope).build();
618
619 let result = registry.create::<TestService>(&ctx).await;
620 assert!(result.is_err());
621 }
622
623 #[rstest]
625 fn test_duplicate_registration_panics() {
626 let registry = DependencyRegistry::new();
627
628 registry.register_async::<TestService, _, _>(DependencyScope::Singleton, |_ctx| async {
629 Ok(TestService { value: 1 })
630 });
631
632 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
634 registry.register_async::<TestService, _, _>(DependencyScope::Request, |_ctx| async {
635 Ok(TestService { value: 2 })
636 });
637 }));
638
639 let err = result.expect_err("expected panic on duplicate registration");
641 let msg = err
642 .downcast_ref::<String>()
643 .map(|s| s.as_str())
644 .or_else(|| err.downcast_ref::<&str>().copied())
645 .expect("panic payload should be a string");
646 assert!(
647 msg.contains("Duplicate DependencyRegistry registration"),
648 "missing duplicate prefix: {msg}"
649 );
650 assert!(
651 msg.contains("TestService"),
652 "missing type name in panic message: {msg}"
653 );
654 assert!(
655 msg.contains("newtype"),
656 "missing newtype hint in panic message: {msg}"
657 );
658 }
659
660 #[rstest]
662 fn test_is_registered_guard_allows_skip() {
663 let registry = DependencyRegistry::new();
664
665 registry.register_async::<TestService, _, _>(DependencyScope::Singleton, |_ctx| async {
666 Ok(TestService { value: 1 })
667 });
668
669 if !registry.is_registered::<TestService>() {
671 registry.register_async::<TestService, _, _>(DependencyScope::Request, |_ctx| async {
672 Ok(TestService { value: 2 })
673 });
674 }
675
676 assert!(registry.is_registered::<TestService>());
677 }
678}