ruice/
bind.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::sync::Arc;
4
5use async_trait::async_trait;
6
7use crate::{AsyncResolve, AsyncResolver, AsyncServices, Resolve, ServiceContainer, Services};
8
9pub struct Bound<Interface>
10where
11    Interface: ?Sized,
12{
13    service: Arc<Interface>,
14}
15
16impl<Interface> From<Arc<Interface>> for Bound<Interface>
17where
18    Interface: ?Sized,
19{
20    fn from(value: Arc<Interface>) -> Self {
21        Self { service: value }
22    }
23}
24
25impl<Interface, C> Resolve<Interface, C> for Bound<Interface>
26where
27    Interface: ?Sized + Send + Sync,
28{
29    fn resolve(&self, _container: &C) -> Option<Arc<Interface>> {
30        Some(Arc::clone(&self.service))
31    }
32}
33
34pub struct BindBy<Interface, C = ServiceContainer>
35where
36    Interface: ?Sized + Send + Sync,
37{
38    #[allow(clippy::type_complexity)]
39    f: Arc<dyn Fn(&C) -> Option<Arc<Interface>> + Send + Sync>,
40}
41
42impl<Interface, C, F> From<F> for BindBy<Interface, C>
43where
44    Interface: ?Sized + Send + Sync,
45    F: (Fn(&C) -> Option<Arc<Interface>>) + Send + Sync + 'static,
46{
47    fn from(value: F) -> Self {
48        Self { f: Arc::new(value) }
49    }
50}
51
52impl<Interface, C> Resolve<Interface, C> for BindBy<Interface, C>
53where
54    Interface: ?Sized + Send + Sync,
55{
56    fn resolve(&self, container: &C) -> Option<Arc<Interface>> {
57        (self.f)(container)
58    }
59}
60
61pub struct AsyncBindBy<Interface, C = ServiceContainer>
62where
63    Interface: ?Sized + Send + Sync,
64{
65    #[allow(clippy::type_complexity)]
66    f: Arc<
67        dyn Fn(&C) -> Pin<Box<dyn Future<Output = Option<Arc<Interface>>> + Send>> + Send + Sync,
68    >,
69}
70
71impl<Interface, C, F, Fut> From<F> for AsyncBindBy<Interface, C>
72where
73    Interface: ?Sized + Send + Sync,
74    F: (Fn(&C) -> Fut) + Send + Sync + 'static,
75    Fut: Future<Output = Option<Arc<Interface>>> + Send + 'static,
76    C: Send + Sync,
77{
78    fn from(value: F) -> Self {
79        Self {
80            f: Arc::new(move |c| Box::pin(value(c))),
81        }
82    }
83}
84
85#[async_trait]
86impl<Interface, C> AsyncResolve<Interface, C> for AsyncBindBy<Interface, C>
87where
88    Interface: ?Sized + Send + Sync,
89    C: Send + Sync,
90{
91    async fn async_resolve(&self, container: &C) -> Option<Arc<Interface>> {
92        (self.f)(container).await
93    }
94}
95
96pub trait BindServices: Services {
97    fn bind<Interface>(&mut self, service: Arc<Interface>)
98    where
99        Interface: ?Sized + Send + Sync + 'static,
100    {
101        self.put(Bound::from(service));
102    }
103
104    fn bind_by<Interface, F>(&mut self, f: F)
105    where
106        Interface: ?Sized + Send + Sync + 'static,
107        F: (Fn(&Self) -> Option<Arc<Interface>>) + Send + Sync + 'static,
108        Self: 'static,
109    {
110        self.put(BindBy::from(f))
111    }
112}
113
114impl<C> BindServices for C where C: Services {}
115
116pub trait AsyncBindServices: AsyncServices {
117    fn bind_by_async<Interface, F, Fut>(&mut self, f: F)
118    where
119        Interface: ?Sized + Send + Sync + 'static,
120        F: (Fn(&Self) -> Fut) + Send + Sync + 'static,
121        Fut: Future<Output = Option<Arc<Interface>>> + Send + 'static,
122        Self: Send + Sync + 'static,
123    {
124        self.put_async(AsyncResolver::new(AsyncBindBy::from(f)))
125    }
126}
127
128impl<C> AsyncBindServices for C where C: AsyncServices {}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133    use crate::ServiceContainer;
134
135    trait Greet: Send + Sync {
136        fn greet(&self) -> String;
137    }
138
139    struct Greeter {
140        name: String,
141    }
142
143    impl Greet for Greeter {
144        fn greet(&self) -> String {
145            format!("Hello, {}!", self.name)
146        }
147    }
148
149    #[test]
150    fn bind() {
151        let mut container = ServiceContainer::default();
152
153        // We can bind a service onto an interface.
154        container.bind::<dyn Greet>(Arc::new(Greeter {
155            name: "Taro".to_string(),
156        }));
157
158        // Now we can get the service by their interface instead of the actual type.
159        let name_getter = container.get::<dyn Greet>().unwrap();
160        assert_eq!("Hello, Taro!".to_string(), name_getter.greet());
161    }
162
163    #[test]
164    fn bind_by() {
165        let mut container = ServiceContainer::default();
166
167        // We can bind a service onto an interface lazily.
168        container.bind_by(|_| -> Option<Arc<dyn Greet>> {
169            Some(Arc::new(Greeter {
170                name: "Taro".to_string(),
171            }))
172        });
173
174        // Now we can get the service by their interface instead of the actual type.
175        let name_getter = container.get::<dyn Greet>().unwrap();
176        assert_eq!("Hello, Taro!".to_string(), name_getter.greet());
177    }
178
179    #[tokio::test]
180    async fn bind_by_async() {
181        let mut container = ServiceContainer::default();
182
183        // We can bind a service onto an interface lazily in an async context.
184        container.bind_by_async(|_| async {
185            Some(Arc::new(Greeter {
186                name: "Taro".to_string(),
187            }) as Arc<dyn Greet>)
188        });
189
190        // We can not the service in a non-async context.
191        assert!(container.get::<dyn Greet>().is_none());
192
193        // Now we can get the service by their interface instead of the actual type.
194        let name_getter = container.get_async::<dyn Greet>().await.unwrap();
195        assert_eq!("Hello, Taro!".to_string(), name_getter.greet());
196    }
197}