elemental_object/
facade.rs1use std::{
2 any::{Any, type_name},
3 future::Future,
4 pin::Pin,
5 sync::Arc,
6};
7
8use anyhow::{Context, Result};
9
10pub use crate::context::ObjectContext;
11use crate::pool::{ObjectPool, POOL, ShutdownFn};
12use crate::{context::CONTEXT, instant::InstantObject};
13
14pub async fn require<T: Any + Send + Sync>() -> Result<Arc<T>> {
16 if let Some(pool) = context_pool()
17 && let Ok(value) = pool.get_async::<T>().await.context(format!(
18 "Cannot get object `{}` from pool",
19 type_name::<T>()
20 ))
21 {
22 return Ok(value);
23 }
24
25 POOL.get_async::<T>().await.context(format!(
26 "Cannot get object `{}` from pool",
27 type_name::<T>()
28 ))
29}
30
31pub async fn provide<T>(value: T)
34where
35 T: Any + Send + Sync,
36{
37 provide_arc::<T>(Arc::new(value), None, false).await;
38}
39
40pub async fn provide_with_shutdown<T, F, Fut>(value: T, shutdown: F)
41where
42 T: Any + Send + Sync,
43 F: Fn(Arc<T>) -> Fut + Send + Sync + 'static,
44 Fut: Future<Output = ()> + Send + 'static,
45{
46 provide_arc(Arc::new(value), Some(convert_shutdown_fn(shutdown)), false).await;
47}
48
49pub async fn provide_context<T>(value: T)
50where
51 T: Any + Send + Sync,
52{
53 provide_arc(Arc::new(value), None, true).await;
54}
55
56pub async fn provide_context_with_shutdown<T, F, Fut>(value: T, shutdown: F)
57where
58 T: Any + Send + Sync,
59 F: Fn(Arc<T>) -> Fut + Send + Sync + 'static,
60 Fut: Future<Output = ()> + Send + 'static,
61{
62 provide_arc(Arc::new(value), Some(convert_shutdown_fn(shutdown)), true).await;
63}
64
65pub async fn provide_instant<T>()
66where
67 T: InstantObject,
68{
69 provide_instant_impl::<T>(false).await;
70}
71
72pub async fn provide_instant_context<T>()
73where
74 T: InstantObject,
75{
76 provide_instant_impl::<T>(true).await;
77}
78
79fn convert_shutdown_fn<T, F, Fut>(shutdown: F) -> ShutdownFn<T>
80where
81 T: Any + Send + Sync,
82 F: Fn(Arc<T>) -> Fut + Send + Sync + 'static,
83 Fut: Future<Output = ()> + Send + 'static,
84{
85 Box::new(move |value: Arc<T>| {
86 Box::pin(shutdown(value)) as Pin<Box<dyn Future<Output = ()> + Send>>
87 })
88}
89async fn provide_arc<T>(value: Arc<T>, shutdown: Option<ShutdownFn<T>>, context: bool)
91where
92 T: Any + Send + Sync,
93{
94 if let Some(pool) = selected_context_pool(context) {
96 pool.set_value(value, shutdown).await;
97 return;
98 }
99
100 POOL.set_value(value, shutdown).await;
101}
102
103pub async fn drop_value<T: Any + Send + Sync>() {
104 POOL.remove_value::<T>().await;
105}
106
107pub async fn drop_context_value<T: Any + Send + Sync>() {
108 if let Some(pool) = context_pool() {
109 pool.remove_value::<T>().await;
110 }
111}
112
113pub async fn drop_entry<T: Any + Send + Sync>() {
114 POOL.remove_entry::<T>().await;
115}
116
117pub async fn drop_context_entry<T: Any + Send + Sync>() {
118 if let Some(pool) = context_pool() {
119 pool.remove_entry::<T>().await;
120 }
121}
122
123#[cfg(feature = "notify")]
128pub async fn acquire<T: Any + Send + Sync>() -> Result<Arc<T>> {
129 if let Some(pool) = context_pool() {
131 pool.wait_value::<T>().await;
132 } else {
133 POOL.wait_value::<T>().await;
134 }
135 require::<T>().await
136}
137
138pub async fn fulfill<T: Any + Send + Sync>(value: T) {
140 fulfill_arc(Arc::new(value), None, false).await;
141}
142
143pub async fn fulfill_with_shutdown<T, F, Fut>(value: T, shutdown: F)
144where
145 T: Any + Send + Sync,
146 F: Fn(Arc<T>) -> Fut + Send + Sync + 'static,
147 Fut: Future<Output = ()> + Send + 'static,
148{
149 fulfill_arc(Arc::new(value), Some(convert_shutdown_fn(shutdown)), false).await;
150}
151
152pub async fn fulfill_context<T: Any + Send + Sync>(value: T) {
153 fulfill_arc(Arc::new(value), None, true).await;
154}
155
156pub async fn fulfill_with_context_shutdown<T, F, Fut>(value: T, shutdown: F)
157where
158 T: Any + Send + Sync,
159 F: Fn(Arc<T>) -> Fut + Send + Sync + 'static,
160 Fut: Future<Output = ()> + Send + 'static,
161{
162 fulfill_arc(Arc::new(value), Some(convert_shutdown_fn(shutdown)), true).await;
163}
164
165async fn fulfill_arc<T: Any + Send + Sync>(
166 value: Arc<T>,
167 shutdown: Option<ShutdownFn<T>>,
168 context: bool,
169) {
170 if let Some(pool) = selected_context_pool(context) {
171 pool.fulfill_value(value, shutdown).await;
172 return;
173 }
174 POOL.fulfill_value(value, shutdown).await;
175}
176
177pub async fn shutdown() {
178 POOL.shutdown().await;
179}
180
181pub async fn shutdown_local() {
182 if let Some(pool) = context_pool() {
183 pool.shutdown().await;
184 }
185}
186
187fn context_pool() -> Option<Arc<ObjectPool>> {
188 CONTEXT.try_with(|pool| pool.clone()).ok()
189}
190
191fn selected_context_pool(context: bool) -> Option<Arc<ObjectPool>> {
192 if context {
193 return context_pool();
194 }
195
196 None
197}
198
199async fn provide_instant_impl<T>(context: bool)
200where
201 T: InstantObject,
202{
203 let instant_value = T::create().await;
204 let shutdown = |value: Arc<T::Output>| async move {
205 T::destroy(value).await;
206 };
207
208 provide_arc(
209 Arc::new(instant_value),
210 Some(convert_shutdown_fn(shutdown)),
211 context,
212 )
213 .await;
214}