Skip to main content

reifydb_core/util/ioc/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4pub mod resolve_arc;
5pub mod resolve_rc;
6
7use std::{
8	any::{Any, TypeId, type_name},
9	collections::HashMap,
10	sync::{Arc, RwLock},
11};
12
13use reifydb_type::Result;
14
15use crate::internal_error;
16
17struct BoxedValue {
18	value: Box<dyn Any + Send + Sync>,
19}
20
21impl BoxedValue {
22	fn new<T: Clone + Any + Send + Sync + 'static>(value: T) -> Self {
23		Self {
24			value: Box::new(value),
25		}
26	}
27
28	fn value<T: Clone + Any + Send + Sync + 'static>(&self) -> Option<T> {
29		self.value.downcast_ref::<T>().cloned()
30	}
31}
32
33/// Lightweight IoC container for dependency injection
34pub struct IocContainer {
35	dependencies: Arc<RwLock<HashMap<TypeId, BoxedValue>>>,
36}
37
38impl IocContainer {
39	pub fn new() -> Self {
40		Self {
41			dependencies: Arc::new(RwLock::new(HashMap::new())),
42		}
43	}
44
45	pub fn register<T: Clone + Any + Send + Sync + 'static>(self, service: T) -> Self {
46		self.dependencies.write().unwrap().insert(TypeId::of::<T>(), BoxedValue::new(service));
47		self
48	}
49
50	/// Register a service from a reference (for late registration after construction)
51	pub fn register_service<T: Clone + Any + Send + Sync + 'static>(&self, service: T) {
52		self.dependencies.write().unwrap().insert(TypeId::of::<T>(), BoxedValue::new(service));
53	}
54
55	pub fn clear(&self) {
56		self.dependencies.write().unwrap().clear();
57	}
58
59	pub fn resolve<T: Clone + Any + Send + Sync + 'static>(&self) -> Result<T> {
60		self.dependencies
61			.read()
62			.unwrap()
63			.get(&TypeId::of::<T>())
64			.and_then(|boxed| boxed.value::<T>())
65			.ok_or_else(|| internal_error!("Type {} not registered in IoC container", type_name::<T>()))
66	}
67
68	/// Resolve a registered service if present. Unlike `resolve`, returns
69	/// `None` when the type is absent without producing a diagnostic - for
70	/// callers that treat absence as a normal outcome.
71	pub fn try_resolve<T: Clone + Any + Send + Sync + 'static>(&self) -> Option<T> {
72		self.dependencies.read().unwrap().get(&TypeId::of::<T>()).and_then(|boxed| boxed.value::<T>())
73	}
74}
75
76impl Clone for IocContainer {
77	fn clone(&self) -> Self {
78		Self {
79			dependencies: self.dependencies.clone(),
80		}
81	}
82}
83
84impl Default for IocContainer {
85	fn default() -> Self {
86		Self::new()
87	}
88}