Skip to main content

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 it's required to construct a `struct` from other dependencies, the `Inject` can be implemented manually
9///
10/// # Example
11/// ```ignore
12/// use volga::{
13///     App,
14///     error::Error,
15///     di::{Dc, Inject, Container},
16///     ok
17/// };
18///
19/// #[derive(Default, Clone)]
20/// struct ScopedService;
21///
22/// #[derive(Clone)]
23/// struct TransientService {
24///     service: ScopedService 
25/// }
26///
27/// impl Inject for TransientService {
28///     fn inject(container: &Container) -> Result<Self, Error> {
29///         let scoped_service = container
30///             .resolve::<ScopedService>()?;
31///         Ok(Self { service: scoped_service })
32///     }
33/// }
34///
35/// let mut app = App::new();
36/// app.add_scoped::<ScopedService>();
37/// app.add_transient::<TransientService>();
38///
39/// app.map_get("/route", |transient_service: Dc<TransientService>| async move {
40///     let scoped = &transient_service.service;
41///     // Do something with scoped and/or transient service
42///     ok!()
43/// });
44/// ```
45pub trait Inject: Sized + Send + Sync {
46    /// Constructs a type with dependencies
47    fn inject(container: &Container) -> Result<Self, Error>;
48}
49
50impl Inject for Container {
51    #[inline]
52    fn inject(container: &Container) -> Result<Self, Error> {
53        Ok(container.clone())
54    }
55}
56
57impl Inject for () {
58    #[inline]
59    fn inject(_: &Container) -> Result<Self, Error> {
60        Ok(())
61    }
62}
63
64macro_rules! define_inject {
65    ($($T: ident),*) => {
66        impl<$($T: Inject),+> Inject for ($($T,)+) {
67            #[inline]
68            #[allow(non_snake_case)]
69            fn inject(container: &Container) -> Result<Self, Error> {
70                let tuple = (
71                    $(
72                    $T::inject(container)?,
73                    )*    
74                );
75                Ok(tuple)
76            }
77        }
78    }
79}
80
81define_inject! { T1 }
82define_inject! { T1, T2 }
83define_inject! { T1, T2, T3 }
84define_inject! { T1, T2, T3, T4 }
85define_inject! { T1, T2, T3, T4, T5 }
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90    use crate::container::ContainerBuilder;
91    use std::sync::{Arc, Mutex};
92
93    #[derive(Default, Clone)]
94    struct SimpleService {
95        value: i32,
96    }
97
98    impl Inject for SimpleService {
99        fn inject(_: &Container) -> Result<Self, Error> {
100            Ok(Default::default())
101        }
102    }
103
104    #[derive(Clone)]
105    struct ServiceWithDependency {
106        service: SimpleService,
107        multiplier: i32,
108    }
109
110    impl Inject for ServiceWithDependency {
111        fn inject(container: &Container) -> Result<Self, Error> {
112            let service = container.resolve::<SimpleService>()?;
113            Ok(Self {
114                service,
115                multiplier: 2,
116            })
117        }
118    }
119
120    #[derive(Clone)]
121    struct ComplexService {
122        dependency: ServiceWithDependency,
123        data: Arc<Mutex<Vec<String>>>,
124    }
125
126    impl Inject for ComplexService {
127        fn inject(container: &Container) -> Result<Self, Error> {
128            let dependency = container.resolve::<ServiceWithDependency>()?;
129            Ok(Self {
130                dependency,
131                data: Arc::new(Mutex::new(vec!["test".to_string()])),
132            })
133        }
134    }
135
136    #[derive(Debug)]
137    struct FailingService;
138
139    impl Inject for FailingService {
140        fn inject(_: &Container) -> Result<Self, Error> {
141            Err(Error::Other("Injection failed"))
142        }
143    }
144    
145    #[test]
146    fn it_injects_default_service() {
147        let container = ContainerBuilder::new().build();
148
149        let result = SimpleService::inject(&container);
150
151        assert!(result.is_ok());
152        let service = result.unwrap();
153        assert_eq!(service.value, 0);
154    }
155
156    #[test]
157    #[allow(clippy::redundant_closure)]
158    fn it_injects_service_with_dependencies() {
159        let mut builder = ContainerBuilder::new();
160        builder.register_scoped_factory(|c: Container| SimpleService::inject(&c));
161        let container = builder.build().create_scope();
162
163        let result = ServiceWithDependency::inject(&container);
164
165        assert!(result.is_ok());
166        let service = result.unwrap();
167        assert_eq!(service.service.value, 0);
168        assert_eq!(service.multiplier, 2);
169    }
170
171    #[test]
172    fn it_injects_complex_service_with_nested_dependencies() {
173        let mut builder = ContainerBuilder::new();
174        builder.register_scoped::<SimpleService>();
175        builder.register_scoped::<ServiceWithDependency>();
176        let container = builder.build().create_scope();
177
178        let result = ComplexService::inject(&container);
179
180        assert!(result.is_ok());
181        let service = result.unwrap();
182        assert_eq!(service.dependency.service.value, 0);
183        assert_eq!(service.dependency.multiplier, 2);
184        let data = service.data.lock().unwrap();
185        assert_eq!(data[0], "test");
186    }
187
188    #[test]
189    fn it_fails_when_dependency_not_registered() {
190        let container = ContainerBuilder::new().build();
191
192        let result = ServiceWithDependency::inject(&container);
193
194        assert!(result.is_err());
195    }
196
197    #[test]
198    fn it_handles_injection_errors() {
199        let container = ContainerBuilder::new().build();
200
201        let result = FailingService::inject(&container);
202
203        assert!(result.is_err());
204        match result.unwrap_err() {
205            Error::Other(msg) => assert_eq!(msg, "Injection failed"),
206            _ => panic!("Expected Other error"),
207        }
208    }
209
210    #[test]
211    fn it_uses_default_trait_implementation_for_inject() {
212        let container = ContainerBuilder::new().build();
213
214        // Test that the blanket implementation works for Default types
215        let result = <SimpleService as Inject>::inject(&container);
216
217        assert!(result.is_ok());
218        let service = result.unwrap();
219        assert_eq!(service.value, 0);
220    }
221
222    #[test]
223    fn it_resolves_same_dependency_multiple_times() {
224        let mut builder = ContainerBuilder::new();
225        builder.register_scoped::<SimpleService>();
226        let container = builder.build().create_scope();
227
228        let result1 = ServiceWithDependency::inject(&container);
229        let result2 = ServiceWithDependency::inject(&container);
230
231        assert!(result1.is_ok());
232        assert!(result2.is_ok());
233
234        let service1 = result1.unwrap();
235        let service2 = result2.unwrap();
236
237        // Both should have the same underlying service instance (scoped)
238        assert_eq!(service1.service.value, service2.service.value);
239        assert_eq!(service1.multiplier, service2.multiplier);
240    }
241
242    #[test]
243    fn it_works_with_different_service_lifetimes() {
244        let mut builder = ContainerBuilder::new();
245        builder.register_singleton(SimpleService { value: 100 });
246        builder.register_transient::<ServiceWithDependency>();
247        let container = builder.build();
248
249        let result1 = ServiceWithDependency::inject(&container);
250        let result2 = ServiceWithDependency::inject(&container);
251
252        assert!(result1.is_ok());
253        assert!(result2.is_ok());
254
255        let service1 = result1.unwrap();
256        let service2 = result2.unwrap();
257
258        // Singleton dependency should be the same
259        assert_eq!(service1.service.value, 100);
260        assert_eq!(service2.service.value, 100);
261    }
262
263    #[test]
264    fn it_tests_send_sync_requirements() {
265        fn assert_send_sync<T: Send + Sync>() {}
266
267        // These should compile without issues due to the Send and Sync bounds
268        assert_send_sync::<SimpleService>();
269        assert_send_sync::<ServiceWithDependency>();
270        assert_send_sync::<ComplexService>();
271    }
272}
273