portaldi_core/
traits.rs

1//! Define traits that expose DI apis to user.
2
3use async_trait::async_trait;
4
5use crate::container::DIContainer;
6use crate::globals::INSTANCE;
7use crate::types::DI;
8
9/// Represent DI target type.
10/// It requires thread safety.
11#[cfg(all(target_arch = "wasm32", not(feature = "multi-thread")))]
12pub trait DITarget: 'static {}
13#[cfg(any(not(target_arch = "wasm32"), feature = "multi-thread"))]
14pub trait DITarget: Send + Sync + 'static {}
15
16#[cfg(all(target_arch = "wasm32", not(feature = "multi-thread")))]
17impl<T: 'static> DITarget for T {}
18#[cfg(any(not(target_arch = "wasm32"), feature = "multi-thread"))]
19impl<T: Send + Sync + 'static> DITarget for T {}
20
21/// Add `di` methods for DI target types.
22pub trait DIPortal {
23    /// DI on a container.
24    fn di_on(container: &DIContainer) -> DI<Self>
25    where
26        Self: Sized + DITarget,
27    {
28        container.get_or_init(|| Self::create_for_di(container))
29    }
30
31    /// DI on the global container.
32    #[cfg(any(not(target_arch = "wasm32"), feature = "multi-thread"))]
33    fn di() -> DI<Self>
34    where
35        Self: Sized + DITarget,
36    {
37        Self::di_on(&INSTANCE)
38    }
39    #[cfg(all(target_arch = "wasm32", not(feature = "multi-thread")))]
40    fn di() -> DI<Self>
41    where
42        Self: Sized + DITarget,
43    {
44        Self::di_on(INSTANCE.with(|i| i.clone()).as_ref())
45    }
46
47    /// Create new instance for DI.
48    fn create_for_di(container: &DIContainer) -> Self;
49}
50
51/// Add `di` methods for DI target types that needs async creation.
52#[cfg_attr(all(target_arch = "wasm32", not(feature = "multi-thread")), async_trait(?Send))]
53#[cfg_attr(
54    any(not(target_arch = "wasm32"), feature = "multi-thread"),
55    async_trait
56)]
57pub trait AsyncDIPortal {
58    /// DI on a container.
59    async fn di_on(container: &DIContainer) -> DI<Self>
60    where
61        Self: Sized + DITarget,
62    {
63        container
64            .get_or_init_async(|| Self::create_for_di(container))
65            .await
66    }
67
68    /// DI on the global container.
69    #[cfg(any(not(target_arch = "wasm32"), feature = "multi-thread"))]
70    async fn di() -> DI<Self>
71    where
72        Self: Sized + DITarget,
73    {
74        Self::di_on(&INSTANCE).await
75    }
76    #[cfg(all(target_arch = "wasm32", not(feature = "multi-thread")))]
77    async fn di() -> DI<Self>
78    where
79        Self: Sized + DITarget,
80    {
81        Self::di_on(INSTANCE.with(|i| i.clone()).as_ref()).await
82    }
83
84    /// Create new instance for DI.
85    async fn create_for_di(container: &DIContainer) -> Self;
86}
87
88/// Provides component instance for trait DI types.
89pub trait DIProvider {
90    /// Target trait type.
91    type Output: ?Sized;
92
93    /// DI on a container.
94    fn di_on(container: &DIContainer) -> DI<Self::Output>;
95
96    /// DI on the global container.
97    #[cfg(any(not(target_arch = "wasm32"), feature = "multi-thread"))]
98    fn di() -> DI<Self::Output> {
99        Self::di_on(&INSTANCE)
100    }
101    #[cfg(all(target_arch = "wasm32", not(feature = "multi-thread")))]
102    fn di() -> DI<Self::Output> {
103        Self::di_on(INSTANCE.with(|i| i.clone()).as_ref())
104    }
105}
106
107/// Provides component instance for trait DI types that needs async creation.
108#[cfg_attr(all(target_arch = "wasm32", not(feature = "multi-thread")), async_trait(?Send))]
109#[cfg_attr(
110    any(not(target_arch = "wasm32"), feature = "multi-thread"),
111    async_trait
112)]
113pub trait AsyncDIProvider {
114    /// Target trait type.
115    type Output: ?Sized;
116
117    /// DI on a container.
118    async fn di_on(container: &DIContainer) -> DI<Self::Output>;
119
120    /// DI on the global container.
121    #[cfg(any(not(target_arch = "wasm32"), feature = "multi-thread"))]
122    async fn di() -> DI<Self::Output> {
123        Self::di_on(&INSTANCE).await
124    }
125    #[cfg(all(target_arch = "wasm32", not(feature = "multi-thread")))]
126    async fn di() -> DI<Self::Output> {
127        Self::di_on(INSTANCE.with(|i| i.clone()).as_ref()).await
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    trait FooI {}
136
137    mod sync_test {
138        use super::*;
139
140        struct Hoge;
141        impl DIPortal for Hoge {
142            fn create_for_di(_container: &DIContainer) -> Self {
143                Hoge {}
144            }
145        }
146        impl FooI for Hoge {}
147
148        struct FooIProvider;
149        impl DIProvider for FooIProvider {
150            type Output = dyn FooI;
151            fn di_on(container: &DIContainer) -> DI<Self::Output> {
152                Hoge::di_on(container)
153            }
154        }
155
156        #[test]
157        #[allow(non_snake_case)]
158        fn test_same_instance_for_DIPortal() {
159            let c = DIContainer::new();
160            let hoge1 = Hoge::di_on(&c).as_ref() as *const _;
161            let hoge2 = Hoge::di_on(&c).as_ref() as *const _;
162            assert!(std::ptr::eq(hoge1, hoge2));
163        }
164
165        #[test]
166        #[allow(non_snake_case)]
167        fn test_same_instance_for_DIProvider() {
168            let c = DIContainer::new();
169            let foo1 = FooIProvider::di_on(&c).as_ref() as *const _;
170            let foo2 = FooIProvider::di_on(&c).as_ref() as *const _;
171            assert!(std::ptr::eq(foo1, foo2));
172        }
173    }
174
175    mod async_test {
176        use super::*;
177
178        struct AsyncHoge;
179        #[async_trait]
180        impl AsyncDIPortal for AsyncHoge {
181            async fn create_for_di(_container: &DIContainer) -> Self {
182                AsyncHoge {}
183            }
184        }
185        impl FooI for AsyncHoge {}
186
187        struct AsyncFooIProvider;
188        #[async_trait]
189        impl AsyncDIProvider for AsyncFooIProvider {
190            type Output = dyn FooI;
191            async fn di_on(container: &DIContainer) -> DI<Self::Output> {
192                AsyncHoge::di_on(container).await
193            }
194        }
195
196        #[tokio::test]
197        #[allow(non_snake_case)]
198        async fn test_same_instance_for_AsyncDIPortal() {
199            let c = DIContainer::new();
200            let hoge1 = AsyncHoge::di_on(&c).await.as_ref() as *const _;
201            let hoge2 = AsyncHoge::di_on(&c).await.as_ref() as *const _;
202            println!("check !!! {:?} {:?}", hoge1, hoge2);
203            assert!(std::ptr::eq(hoge1, hoge2));
204        }
205
206        #[tokio::test]
207        #[allow(non_snake_case)]
208        async fn test_same_instance_for_AsyncDIProvider() {
209            let c = DIContainer::new();
210            let foo1 = AsyncFooIProvider::di_on(&c).await.as_ref() as *const _;
211            let foo2 = AsyncFooIProvider::di_on(&c).await.as_ref() as *const _;
212            println!("check !!! {:?} {:?}", foo1, foo2);
213            assert!(std::ptr::eq(foo1, foo2));
214        }
215    }
216}