dependency_injector/
typed.rs1use crate::{Container, Injectable};
60use std::marker::PhantomData;
61use std::sync::Arc;
62
63pub struct Reg<T, Rest>(PhantomData<(T, Rest)>);
69
70pub trait HasType<T: Injectable> {}
72
73impl<T: Injectable, Rest> HasType<T> for Reg<T, Rest> {}
74
75pub struct TypedBuilder<R = ()> {
83 container: Container,
84 _registry: PhantomData<R>,
85}
86
87impl TypedBuilder<()> {
88 #[inline]
90 pub fn new() -> Self {
91 Self {
92 container: Container::new(),
93 _registry: PhantomData,
94 }
95 }
96
97 #[inline]
99 pub fn with_capacity(capacity: usize) -> Self {
100 Self {
101 container: Container::with_capacity(capacity),
102 _registry: PhantomData,
103 }
104 }
105}
106
107impl Default for TypedBuilder<()> {
108 fn default() -> Self {
109 Self::new()
110 }
111}
112
113impl<R> TypedBuilder<R> {
114 #[inline]
116 pub fn singleton<T: Injectable>(self, instance: T) -> TypedBuilder<Reg<T, R>> {
117 self.container.singleton(instance);
118 TypedBuilder {
119 container: self.container,
120 _registry: PhantomData,
121 }
122 }
123
124 #[inline]
126 pub fn lazy<T: Injectable, F>(self, factory: F) -> TypedBuilder<Reg<T, R>>
127 where
128 F: Fn() -> T + Send + Sync + 'static,
129 {
130 self.container.lazy(factory);
131 TypedBuilder {
132 container: self.container,
133 _registry: PhantomData,
134 }
135 }
136
137 #[inline]
139 pub fn transient<T: Injectable, F>(self, factory: F) -> TypedBuilder<Reg<T, R>>
140 where
141 F: Fn() -> T + Send + Sync + 'static,
142 {
143 self.container.transient(factory);
144 TypedBuilder {
145 container: self.container,
146 _registry: PhantomData,
147 }
148 }
149
150 #[inline]
152 pub fn build(self) -> TypedContainer<R> {
153 self.container.lock();
154 TypedContainer {
155 container: self.container,
156 _registry: PhantomData,
157 }
158 }
159
160 #[inline]
162 pub fn build_dynamic(self) -> Container {
163 self.container.lock();
164 self.container
165 }
166
167 #[inline]
169 pub fn inner(&self) -> &Container {
170 &self.container
171 }
172}
173
174pub trait DeclaresDeps: Injectable {
190 fn dependency_names() -> &'static [&'static str] {
192 &[]
193 }
194}
195
196impl<R> TypedBuilder<R> {
197 #[inline]
202 pub fn with_deps<T: DeclaresDeps>(self, instance: T) -> TypedBuilder<Reg<T, R>> {
203 self.singleton(instance)
204 }
205
206 #[inline]
208 pub fn lazy_with_deps<T: DeclaresDeps, F>(self, factory: F) -> TypedBuilder<Reg<T, R>>
209 where
210 F: Fn() -> T + Send + Sync + 'static,
211 {
212 self.lazy(factory)
213 }
214}
215
216pub trait VerifyDeps<D> {}
218impl<R, D> VerifyDeps<D> for R {}
219
220pub struct TypedContainer<R> {
229 container: Container,
230 _registry: PhantomData<R>,
231}
232
233impl<R> TypedContainer<R> {
234 #[inline]
238 pub fn get<T: Injectable>(&self) -> Arc<T> {
239 self.container
240 .get::<T>()
241 .expect("TypedContainer: service not found (registration mismatch)")
242 }
243
244 #[inline]
246 pub fn try_get<T: Injectable>(&self) -> Option<Arc<T>> {
247 self.container.try_get::<T>()
248 }
249
250 #[inline]
252 pub fn contains<T: Injectable>(&self) -> bool {
253 self.container.contains::<T>()
254 }
255
256 #[inline]
258 pub fn scope(&self) -> Container {
259 self.container.scope()
260 }
261
262 #[inline]
264 pub fn inner(&self) -> &Container {
265 &self.container
266 }
267
268 #[inline]
270 pub fn into_inner(self) -> Container {
271 self.container
272 }
273}
274
275impl<R> Clone for TypedContainer<R> {
276 fn clone(&self) -> Self {
277 Self {
278 container: self.container.clone(),
279 _registry: PhantomData,
280 }
281 }
282}
283
284impl<R> std::fmt::Debug for TypedContainer<R> {
285 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286 f.debug_struct("TypedContainer")
287 .field("inner", &self.container)
288 .finish()
289 }
290}
291
292pub trait Has<T: Injectable>: HasType<T> {}
298impl<T: Injectable, R: HasType<T>> Has<T> for R {}
299
300pub trait HasService<T: Injectable>: HasType<T> {}
302impl<T: Injectable, R: HasType<T>> HasService<T> for R {}
303
304pub trait DepsPresent<D> {}
306impl<R, D> DepsPresent<D> for R {}
307
308#[cfg(test)]
313mod tests {
314 use super::*;
315
316 #[derive(Clone)]
317 struct Database {
318 url: String,
319 }
320
321 #[derive(Clone)]
322 struct Cache {
323 size: usize,
324 }
325
326 #[derive(Clone)]
327 struct UserService;
328
329 #[test]
330 fn test_typed_builder_basic() {
331 let container = TypedBuilder::new()
332 .singleton(Database {
333 url: "postgres://localhost".into(),
334 })
335 .singleton(Cache { size: 1024 })
336 .build();
337
338 let db = container.get::<Database>();
339 let cache = container.get::<Cache>();
340
341 assert_eq!(db.url, "postgres://localhost");
342 assert_eq!(cache.size, 1024);
343 }
344
345 #[test]
346 fn test_typed_builder_lazy() {
347 let container = TypedBuilder::new()
348 .lazy(|| Database {
349 url: "lazy://created".into(),
350 })
351 .build();
352
353 let db = container.get::<Database>();
354 assert_eq!(db.url, "lazy://created");
355 }
356
357 #[test]
358 fn test_typed_builder_transient() {
359 use std::sync::atomic::{AtomicU32, Ordering};
360
361 static COUNTER: AtomicU32 = AtomicU32::new(0);
362
363 #[derive(Clone)]
364 struct Counter(u32);
365
366 let container = TypedBuilder::new()
367 .transient(|| Counter(COUNTER.fetch_add(1, Ordering::SeqCst)))
368 .build();
369
370 let c1 = container.get::<Counter>();
371 let c2 = container.get::<Counter>();
372
373 assert_ne!(c1.0, c2.0);
374 }
375
376 #[test]
377 fn test_typed_container_clone() {
378 let container = TypedBuilder::new()
379 .singleton(Database { url: "test".into() })
380 .build();
381
382 let container2 = container.clone();
383
384 let db1 = container.get::<Database>();
385 let db2 = container2.get::<Database>();
386
387 assert!(Arc::ptr_eq(&db1, &db2));
388 }
389
390 #[test]
391 fn test_with_dependencies() {
392 impl DeclaresDeps for UserService {
393 fn dependency_names() -> &'static [&'static str] {
394 &["Database", "Cache"]
395 }
396 }
397
398 let container = TypedBuilder::new()
400 .singleton(Database { url: "pg".into() })
401 .singleton(Cache { size: 100 })
402 .with_deps(UserService)
403 .build();
404
405 let _ = container.get::<UserService>();
406 }
407
408 #[test]
409 fn test_many_services() {
410 #[derive(Clone)]
411 struct S1;
412 #[derive(Clone)]
413 struct S2;
414 #[derive(Clone)]
415 struct S3;
416 #[derive(Clone)]
417 struct S4;
418 #[derive(Clone)]
419 struct S5;
420
421 let container = TypedBuilder::new()
422 .singleton(S1)
423 .singleton(S2)
424 .singleton(S3)
425 .singleton(S4)
426 .singleton(S5)
427 .build();
428
429 let _ = container.get::<S1>();
430 let _ = container.get::<S2>();
431 let _ = container.get::<S3>();
432 let _ = container.get::<S4>();
433 let _ = container.get::<S5>();
434 }
435
436 #[test]
437 fn test_scope_from_typed() {
438 let container = TypedBuilder::new()
439 .singleton(Database { url: "root".into() })
440 .build();
441
442 let child = container.scope();
443 child.singleton(Cache { size: 256 });
444
445 assert!(child.contains::<Database>());
446 assert!(child.contains::<Cache>());
447 }
448}