dependency_injector/
scope.rs1use crate::{Container, Injectable, Result};
6use std::sync::Arc;
7use std::sync::atomic::{AtomicU64, Ordering};
8
9#[cfg(feature = "logging")]
10use tracing::debug;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct Scope(u64);
17
18impl Scope {
19 #[inline]
21 pub fn new() -> Self {
22 static COUNTER: AtomicU64 = AtomicU64::new(1);
23 Self(COUNTER.fetch_add(1, Ordering::Relaxed))
24 }
25
26 #[inline]
28 pub fn id(&self) -> u64 {
29 self.0
30 }
31}
32
33impl Default for Scope {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl std::fmt::Display for Scope {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 write!(f, "scope-{}", self.0)
42 }
43}
44
45pub struct ScopedContainer {
72 container: Container,
74 scope: Scope,
76}
77
78impl ScopedContainer {
79 #[inline]
81 pub fn new() -> Self {
82 let scope = Scope::new();
83
84 #[cfg(feature = "logging")]
85 debug!(
86 target: "dependency_injector",
87 scope_id = scope.id(),
88 "Creating new root ScopedContainer"
89 );
90
91 Self {
92 container: Container::new(),
93 scope,
94 }
95 }
96
97 #[inline]
99 pub fn from_parent(parent: &Container) -> Self {
100 let scope = Scope::new();
101
102 #[cfg(feature = "logging")]
103 debug!(
104 target: "dependency_injector",
105 scope_id = scope.id(),
106 parent_depth = parent.depth(),
107 "Creating ScopedContainer from parent Container"
108 );
109
110 Self {
111 container: parent.scope(),
112 scope,
113 }
114 }
115
116 #[inline]
118 pub fn from_scope(parent: &ScopedContainer) -> Self {
119 let scope = Scope::new();
120
121 #[cfg(feature = "logging")]
122 debug!(
123 target: "dependency_injector",
124 scope_id = scope.id(),
125 parent_scope_id = parent.scope.id(),
126 "Creating child ScopedContainer from parent ScopedContainer"
127 );
128
129 Self {
130 container: parent.container.scope(),
131 scope,
132 }
133 }
134
135 #[inline]
137 pub fn scope(&self) -> Scope {
138 self.scope
139 }
140
141 #[inline]
143 pub fn singleton<T: Injectable>(&self, instance: T) {
144 self.container.singleton(instance);
145 }
146
147 #[inline]
149 pub fn lazy<T: Injectable, F>(&self, factory: F)
150 where
151 F: Fn() -> T + Send + Sync + 'static,
152 {
153 self.container.lazy(factory);
154 }
155
156 #[inline]
158 pub fn transient<T: Injectable, F>(&self, factory: F)
159 where
160 F: Fn() -> T + Send + Sync + 'static,
161 {
162 self.container.transient(factory);
163 }
164
165 #[inline]
167 pub fn register<T: Injectable>(&self, instance: T) {
168 self.container.register(instance);
169 }
170
171 #[inline]
173 pub fn register_factory<T: Injectable, F>(&self, factory: F)
174 where
175 F: Fn() -> T + Send + Sync + 'static,
176 {
177 self.container.register_factory(factory);
178 }
179
180 #[inline]
182 pub fn get<T: Injectable>(&self) -> Result<Arc<T>> {
183 self.container.get::<T>()
184 }
185
186 #[inline]
188 pub fn resolve<T: Injectable>(&self) -> Result<Arc<T>> {
189 self.get::<T>()
190 }
191
192 #[inline]
194 pub fn try_get<T: Injectable>(&self) -> Option<Arc<T>> {
195 self.container.try_get::<T>()
196 }
197
198 #[inline]
200 pub fn try_resolve<T: Injectable>(&self) -> Option<Arc<T>> {
201 self.try_get::<T>()
202 }
203
204 #[inline]
206 pub fn contains<T: Injectable>(&self) -> bool {
207 self.container.contains::<T>()
208 }
209
210 #[inline]
212 pub fn has<T: Injectable>(&self) -> bool {
213 self.contains::<T>()
214 }
215
216 #[inline]
218 pub fn container(&self) -> &Container {
219 &self.container
220 }
221
222 #[inline]
224 pub fn container_mut(&mut self) -> &mut Container {
225 &mut self.container
226 }
227
228 #[inline]
230 pub fn depth(&self) -> u32 {
231 self.container.depth()
232 }
233}
234
235impl Default for ScopedContainer {
236 fn default() -> Self {
237 Self::new()
238 }
239}
240
241impl std::fmt::Debug for ScopedContainer {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 f.debug_struct("ScopedContainer")
244 .field("scope", &self.scope)
245 .field("container", &self.container)
246 .finish()
247 }
248}
249
250pub struct ScopeBuilder {
276 #[allow(clippy::type_complexity)]
277 factories: Vec<Box<dyn Fn(&Container) + Send + Sync>>,
278}
279
280impl ScopeBuilder {
281 #[inline]
283 pub fn new() -> Self {
284 Self {
285 factories: Vec::new(),
286 }
287 }
288
289 pub fn with_singleton<T, F>(mut self, factory: F) -> Self
291 where
292 T: Injectable + Clone,
293 F: Fn() -> T + Send + Sync + 'static,
294 {
295 self.factories.push(Box::new(move |container| {
296 container.singleton(factory());
297 }));
298 self
299 }
300
301 pub fn with_lazy<T, F>(mut self, factory: F) -> Self
303 where
304 T: Injectable,
305 F: Fn() -> T + Send + Sync + Clone + 'static,
306 {
307 self.factories.push(Box::new(move |container| {
308 let f = factory.clone();
309 container.lazy(f);
310 }));
311 self
312 }
313
314 pub fn with_transient<T, F>(mut self, factory: F) -> Self
316 where
317 T: Injectable,
318 F: Fn() -> T + Send + Sync + Clone + 'static,
319 {
320 self.factories.push(Box::new(move |container| {
321 let f = factory.clone();
322 container.transient(f);
323 }));
324 self
325 }
326
327 pub fn build(&self, parent: &Container) -> ScopedContainer {
329 let scoped = ScopedContainer::from_parent(parent);
330 for factory in &self.factories {
331 factory(&scoped.container);
332 }
333 scoped
334 }
335}
336
337impl Default for ScopeBuilder {
338 fn default() -> Self {
339 Self::new()
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[derive(Clone)]
348 struct GlobalService;
349
350 #[derive(Clone)]
351 struct RequestService {
352 id: String,
353 }
354
355 #[test]
356 fn test_scoped_container() {
357 let root = Container::new();
358 root.singleton(GlobalService);
359
360 let scoped = ScopedContainer::from_parent(&root);
361 scoped.singleton(RequestService { id: "req-1".into() });
362
363 assert!(scoped.contains::<GlobalService>());
365 assert!(scoped.contains::<RequestService>());
366
367 assert!(!root.contains::<RequestService>());
369 }
370
371 #[test]
372 fn test_scope_ids_unique() {
373 let s1 = Scope::new();
374 let s2 = Scope::new();
375 let s3 = Scope::new();
376
377 assert_ne!(s1.id(), s2.id());
378 assert_ne!(s2.id(), s3.id());
379 }
380
381 #[test]
382 fn test_scope_builder() {
383 let root = Container::new();
384 root.singleton(GlobalService);
385
386 let builder = ScopeBuilder::new().with_singleton(|| RequestService { id: "built".into() });
387
388 let scoped = builder.build(&root);
389
390 assert!(scoped.contains::<GlobalService>());
391 assert!(scoped.contains::<RequestService>());
392
393 let req = scoped.get::<RequestService>().unwrap();
394 assert_eq!(req.id, "built");
395 }
396
397 #[test]
398 fn test_scope_display() {
399 let scope = Scope::new();
400 let display = format!("{}", scope);
401 assert!(display.starts_with("scope-"));
402 }
403}