volga_di/
inject.rs

1//! Utilities to inject and resolve dependencies
2
3use crate::Container;
4use crate::error::Error;
5
6/// A trait that adds the ability to inject dependencies when resolving a type from the DI container
7///
8/// If there is no need to inject other dependencies, the `struct` must implement the `Default` trait
9///
10/// # Example
11/// ```ignore
12/// use volga::{App, di::Dc, ok};
13///
14/// #[derive(Default, Clone)]
15/// struct ScopedService;
16///
17/// let mut app = App::new();
18/// app.add_scoped::<ScopedService>();
19///
20/// app.map_get("/route", |scoped_service: Dc<ScopedService>| async move {
21///     // Do something with scoped service
22///     ok!()
23/// });
24/// ```
25///
26/// If it's required to construct a `struct` from other dependencies, the `Inject` can be implemented manually
27///
28/// # Example
29/// ```ignore
30/// use volga::{
31///     App,
32///     error::Error,
33///     di::{Dc, Inject, Container},
34///     ok
35/// };
36///
37/// #[derive(Default, Clone)]
38/// struct ScopedService;
39///
40/// #[derive(Clone)]
41/// struct TransientService {
42///     service: ScopedService 
43/// }
44///
45/// impl Inject for TransientService {
46///     fn inject(container: &Container) -> Result<Self, Error> {
47///         let scoped_service = container
48///             .resolve::<ScopedService>()?;
49///         Ok(Self { service: scoped_service })
50///     }
51/// }
52///
53/// let mut app = App::new();
54/// app.add_scoped::<ScopedService>();
55/// app.add_transient::<TransientService>();
56///
57/// app.map_get("/route", |transient_service: Dc<TransientService>| async move {
58///     let scoped = &transient_service.service;
59///     // Do something with scoped and/or transient service
60///     ok!()
61/// });
62/// ```
63pub trait Inject: Sized + Send + Sync {
64    fn inject(container: &Container) -> Result<Self, Error>;
65}
66
67impl<T: Default + Send + Sync> Inject for T {
68    #[inline]
69    fn inject(_: &Container) -> Result<Self, Error> {
70        Ok(Self::default())
71    }
72}
73
74/// A `singleton!` macro that simplifies implementing the [`Inject`] trait for one or more singleton types.
75///
76/// # Macro Syntax
77/// ```ignore
78/// singleton! {
79///     Type1 
80///     Type2 
81///     …
82///     TypeN
83/// };
84/// ```
85/// Each `Type` corresponds to a type for which the `Inject` trait implementation 
86/// will be provided. The macro allows specifying one or more types.
87///
88/// # Example
89/// ```ignore
90/// use volga::di::{singleton, Error, Inject, Container};
91///
92/// struct MyType;
93/// struct AnotherType;
94///
95/// singleton! { 
96///     MyType
97///     AnotherType
98/// };
99///
100/// // Now `MyType` and `AnotherType` have `Inject` implementations:
101/// 
102/// let mut container = ContainerBuilder::new();
103/// container.register_singleton::<MyType>();
104/// container.register_singleton::<AnotherType>();
105/// 
106/// let container = container.build();
107/// let result: Result<MyType, Error> = MyType::inject(&container);
108/// 
109/// assert!(matches!(result, Err(Error::ResolveFailed("MyType"))));
110/// ```
111///
112/// # Behavior
113/// - `inject` will always return `Err(Error::ResolveFailed)`, where the error's 
114///   message is the name of the type that could not be resolved, as a string.
115///
116/// # Errors
117/// - When attempting to use the `inject` function, the macro-generated implementation 
118///   always produces an error of type `Error::ResolveFailed` because it does not 
119///   define a mechanism for successfully resolving the type.
120#[macro_export]
121macro_rules! singleton {
122    ($($name:ident)*) => {
123        $(impl $crate::Inject for $name {
124            #[inline]
125            fn inject(_: &$crate::Container) -> Result<Self, $crate::error::Error> {
126                Err($crate::error::Error::ResolveFailed(stringify!($name)))
127            }
128        })*      
129    };
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use crate::container::ContainerBuilder;
136    use std::sync::{Arc, Mutex};
137
138    #[derive(Default, Clone)]
139    struct SimpleService {
140        value: i32,
141    }
142
143    #[derive(Clone)]
144    struct ServiceWithDependency {
145        service: SimpleService,
146        multiplier: i32,
147    }
148
149    impl Inject for ServiceWithDependency {
150        fn inject(container: &Container) -> Result<Self, Error> {
151            let service = container.resolve::<SimpleService>()?;
152            Ok(Self {
153                service,
154                multiplier: 2,
155            })
156        }
157    }
158
159    #[derive(Clone)]
160    struct ComplexService {
161        dependency: ServiceWithDependency,
162        data: Arc<Mutex<Vec<String>>>,
163    }
164
165    impl Inject for ComplexService {
166        fn inject(container: &Container) -> Result<Self, Error> {
167            let dependency = container.resolve::<ServiceWithDependency>()?;
168            Ok(Self {
169                dependency,
170                data: Arc::new(Mutex::new(vec!["test".to_string()])),
171            })
172        }
173    }
174
175    #[derive(Debug)]
176    struct FailingService;
177
178    impl Inject for FailingService {
179        fn inject(_: &Container) -> Result<Self, Error> {
180            Err(Error::Other("Injection failed"))
181        }
182    }
183    
184    #[derive(Debug)]
185    struct SingletonService;
186    
187    singleton! {
188        SingletonService
189    }
190
191    #[test]
192    fn it_injects_default_service() {
193        let container = ContainerBuilder::new().build();
194
195        let result = SimpleService::inject(&container);
196
197        assert!(result.is_ok());
198        let service = result.unwrap();
199        assert_eq!(service.value, 0);
200    }
201
202    #[test]
203    fn it_injects_service_with_dependencies() {
204        let mut builder = ContainerBuilder::new();
205        builder.register_scoped::<SimpleService>();
206        let container = builder.build().create_scope();
207
208        let result = ServiceWithDependency::inject(&container);
209
210        assert!(result.is_ok());
211        let service = result.unwrap();
212        assert_eq!(service.service.value, 0);
213        assert_eq!(service.multiplier, 2);
214    }
215
216    #[test]
217    fn it_injects_complex_service_with_nested_dependencies() {
218        let mut builder = ContainerBuilder::new();
219        builder.register_scoped::<SimpleService>();
220        builder.register_scoped::<ServiceWithDependency>();
221        let container = builder.build().create_scope();
222
223        let result = ComplexService::inject(&container);
224
225        assert!(result.is_ok());
226        let service = result.unwrap();
227        assert_eq!(service.dependency.service.value, 0);
228        assert_eq!(service.dependency.multiplier, 2);
229        let data = service.data.lock().unwrap();
230        assert_eq!(data[0], "test");
231    }
232
233    #[test]
234    fn it_fails_when_dependency_not_registered() {
235        let container = ContainerBuilder::new().build();
236
237        let result = ServiceWithDependency::inject(&container);
238
239        assert!(result.is_err());
240    }
241
242    #[test]
243    fn it_handles_injection_errors() {
244        let container = ContainerBuilder::new().build();
245
246        let result = FailingService::inject(&container);
247
248        assert!(result.is_err());
249        match result.unwrap_err() {
250            Error::Other(msg) => assert_eq!(msg, "Injection failed"),
251            _ => panic!("Expected Other error"),
252        }
253    }
254
255    #[test]
256    fn it_uses_default_trait_implementation_for_inject() {
257        let container = ContainerBuilder::new().build();
258
259        // Test that the blanket implementation works for Default types
260        let result = <SimpleService as Inject>::inject(&container);
261
262        assert!(result.is_ok());
263        let service = result.unwrap();
264        assert_eq!(service.value, 0);
265    }
266
267    #[test]
268    fn it_resolves_same_dependency_multiple_times() {
269        let mut builder = ContainerBuilder::new();
270        builder.register_scoped::<SimpleService>();
271        let container = builder.build().create_scope();
272
273        let result1 = ServiceWithDependency::inject(&container);
274        let result2 = ServiceWithDependency::inject(&container);
275
276        assert!(result1.is_ok());
277        assert!(result2.is_ok());
278
279        let service1 = result1.unwrap();
280        let service2 = result2.unwrap();
281
282        // Both should have the same underlying service instance (scoped)
283        assert_eq!(service1.service.value, service2.service.value);
284        assert_eq!(service1.multiplier, service2.multiplier);
285    }
286
287    #[test]
288    fn it_works_with_different_service_lifetimes() {
289        let mut builder = ContainerBuilder::new();
290        builder.register_singleton(SimpleService { value: 100 });
291        builder.register_transient::<ServiceWithDependency>();
292        let container = builder.build();
293
294        let result1 = ServiceWithDependency::inject(&container);
295        let result2 = ServiceWithDependency::inject(&container);
296
297        assert!(result1.is_ok());
298        assert!(result2.is_ok());
299
300        let service1 = result1.unwrap();
301        let service2 = result2.unwrap();
302
303        // Singleton dependency should be the same
304        assert_eq!(service1.service.value, 100);
305        assert_eq!(service2.service.value, 100);
306    }
307
308    #[test]
309    fn it_prevents_singleton_injection() {
310        let container = ContainerBuilder::new().build();
311
312        let result = SingletonService::inject(&container);
313
314        assert!(result.is_err());
315        match result.unwrap_err() {
316            Error::ResolveFailed(msg) => assert_eq!(msg, "SingletonService"),
317            _ => panic!("Expected ResolveFailed error with singleton message"),
318        }
319    }
320
321
322    #[test]
323    fn it_tests_send_sync_requirements() {
324        fn assert_send_sync<T: Send + Sync>() {}
325
326        // These should compile without issues due to the Send + Sync bounds
327        assert_send_sync::<SimpleService>();
328        assert_send_sync::<ServiceWithDependency>();
329        assert_send_sync::<ComplexService>();
330    }
331}
332