Skip to main content

reinhardt_di/
scope.rs

1//! Dependency scopes
2
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5use std::sync::{Arc, PoisonError, RwLock};
6
7/// Defines the lifetime scope of a dependency.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Scope {
10	/// A new instance is created for each request.
11	Request,
12	/// A single instance is shared across the entire application lifetime.
13	Singleton,
14}
15
16/// Per-request dependency cache that stores resolved instances for the duration of a request.
17#[derive(Clone)]
18pub struct RequestScope {
19	cache: Arc<RwLock<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>>,
20}
21
22impl RequestScope {
23	/// Creates a new RequestScope with an empty cache.
24	///
25	/// # Examples
26	///
27	/// ```
28	/// use reinhardt_di::RequestScope;
29	///
30	/// let scope = RequestScope::new();
31	/// ```
32	pub fn new() -> Self {
33		Self {
34			cache: Arc::new(RwLock::new(HashMap::new())),
35		}
36	}
37	/// Retrieves a value from the request scope cache by type.
38	///
39	/// Returns `None` if no value of type `T` exists in the cache.
40	///
41	/// # Examples
42	///
43	/// ```
44	/// use reinhardt_di::RequestScope;
45	///
46	/// let scope = RequestScope::new();
47	/// scope.set(42i32);
48	///
49	/// let value = scope.get::<i32>().unwrap();
50	/// assert_eq!(*value, 42);
51	/// ```
52	pub fn get<T: Any + Send + Sync>(&self) -> Option<Arc<T>> {
53		let cache = self.cache.read().unwrap_or_else(PoisonError::into_inner);
54		let type_id = TypeId::of::<T>();
55		cache
56			.get(&type_id)
57			.and_then(|arc| arc.clone().downcast::<T>().ok())
58	}
59	/// Stores a value in the request scope cache.
60	///
61	/// The value is stored by its type and can be retrieved later using `get`.
62	///
63	/// # Examples
64	///
65	/// ```
66	/// use reinhardt_di::RequestScope;
67	///
68	/// let scope = RequestScope::new();
69	/// scope.set(42i32);
70	/// scope.set("hello".to_string());
71	///
72	/// assert_eq!(*scope.get::<i32>().unwrap(), 42);
73	/// assert_eq!(*scope.get::<String>().unwrap(), "hello");
74	/// ```
75	pub fn set<T: Any + Send + Sync>(&self, value: T) {
76		let mut cache = self.cache.write().unwrap_or_else(PoisonError::into_inner);
77		let type_id = TypeId::of::<T>();
78		cache.insert(type_id, Arc::new(value));
79	}
80
81	/// Stores a pre-wrapped `Arc<T>` in the request scope cache.
82	///
83	/// Unlike `set`, this method accepts an already-wrapped Arc value,
84	/// avoiding the need to unwrap and re-wrap. This is useful when
85	/// the value is produced by a factory that returns `Arc<T>`.
86	///
87	/// # Examples
88	///
89	/// ```
90	/// use reinhardt_di::RequestScope;
91	/// use std::sync::Arc;
92	///
93	/// let scope = RequestScope::new();
94	/// let value = Arc::new(42i32);
95	/// scope.set_arc(value);
96	///
97	/// assert_eq!(*scope.get::<i32>().unwrap(), 42);
98	/// ```
99	pub fn set_arc<T: Any + Send + Sync>(&self, value: Arc<T>) {
100		let mut cache = self.cache.write().unwrap_or_else(PoisonError::into_inner);
101		let type_id = TypeId::of::<T>();
102		cache.insert(type_id, value);
103	}
104}
105
106impl RequestScope {
107	/// Creates a deep clone of this scope with an independent cache.
108	///
109	/// The cloned scope contains the same cached entries as the original,
110	/// but modifications to either scope will not affect the other.
111	pub fn deep_clone(&self) -> Self {
112		let cache = self.cache.read().unwrap_or_else(PoisonError::into_inner);
113		Self {
114			cache: Arc::new(RwLock::new(cache.clone())),
115		}
116	}
117}
118
119impl Default for RequestScope {
120	fn default() -> Self {
121		Self::new()
122	}
123}
124
125/// Application-wide dependency cache that persists across all requests.
126pub struct SingletonScope {
127	cache: Arc<RwLock<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>>,
128}
129
130impl SingletonScope {
131	/// Creates a new SingletonScope with an empty cache.
132	///
133	/// # Examples
134	///
135	/// ```
136	/// use reinhardt_di::SingletonScope;
137	///
138	/// let scope = SingletonScope::new();
139	/// ```
140	pub fn new() -> Self {
141		Self {
142			cache: Arc::new(RwLock::new(HashMap::new())),
143		}
144	}
145	/// Retrieves a singleton value from the cache by type.
146	///
147	/// Returns `None` if no value of type `T` exists in the singleton cache.
148	///
149	/// # Examples
150	///
151	/// ```
152	/// use reinhardt_di::SingletonScope;
153	///
154	/// let scope = SingletonScope::new();
155	/// scope.set(100u64);
156	///
157	/// let value = scope.get::<u64>().unwrap();
158	/// assert_eq!(*value, 100);
159	/// ```
160	pub fn get<T: Any + Send + Sync>(&self) -> Option<Arc<T>> {
161		let cache = self.cache.read().unwrap_or_else(PoisonError::into_inner);
162		let type_id = TypeId::of::<T>();
163		cache
164			.get(&type_id)
165			.and_then(|arc| arc.clone().downcast::<T>().ok())
166	}
167	/// Stores a singleton value in the cache.
168	///
169	/// The value persists across multiple requests and is shared application-wide.
170	///
171	/// # Examples
172	///
173	/// ```
174	/// use reinhardt_di::SingletonScope;
175	///
176	/// let scope = SingletonScope::new();
177	/// scope.set(42i32);
178	///
179	/// // Same value retrieved across multiple calls
180	/// let val1 = scope.get::<i32>().unwrap();
181	/// let val2 = scope.get::<i32>().unwrap();
182	/// assert_eq!(*val1, *val2);
183	/// ```
184	pub fn set<T: Any + Send + Sync>(&self, value: T) {
185		let mut cache = self.cache.write().unwrap_or_else(PoisonError::into_inner);
186		let type_id = TypeId::of::<T>();
187		cache.insert(type_id, Arc::new(value));
188	}
189
190	/// Stores a pre-wrapped `Arc<T>` in the singleton scope cache.
191	///
192	/// Unlike `set`, this method accepts an already-wrapped Arc value,
193	/// avoiding the need to unwrap and re-wrap. This is useful when
194	/// the value is produced by a factory that returns `Arc<T>`.
195	///
196	/// # Examples
197	///
198	/// ```
199	/// use reinhardt_di::SingletonScope;
200	/// use std::sync::Arc;
201	///
202	/// let scope = SingletonScope::new();
203	/// let value = Arc::new(42i32);
204	/// scope.set_arc(value);
205	///
206	/// assert_eq!(*scope.get::<i32>().unwrap(), 42);
207	/// ```
208	pub fn set_arc<T: Any + Send + Sync>(&self, value: Arc<T>) {
209		let mut cache = self.cache.write().unwrap_or_else(PoisonError::into_inner);
210		let type_id = TypeId::of::<T>();
211		cache.insert(type_id, value);
212	}
213
214	/// Inserts a pre-erased `Arc<dyn Any + Send + Sync>` keyed by an explicit
215	/// `TypeId`.
216	///
217	/// This is the type-erased counterpart of [`set_arc`](Self::set_arc) and
218	/// is intended for callers (such as the `Middleware::di_registrations`
219	/// bridge in `reinhardt-urls`) that have already type-erased the value to
220	/// avoid a circular crate dependency on `reinhardt-di`.
221	pub fn set_arc_any(&self, type_id: TypeId, value: Arc<dyn Any + Send + Sync>) {
222		let mut cache = self.cache.write().unwrap_or_else(PoisonError::into_inner);
223		cache.insert(type_id, value);
224	}
225}
226
227impl Default for SingletonScope {
228	fn default() -> Self {
229		Self::new()
230	}
231}