Skip to main content

a3s_boot/provider/
definition.rs

1use super::{AnyProvider, ModuleRef, ProviderToken};
2use crate::{BootError, BoxFuture, Result};
3use std::fmt;
4use std::future::Future;
5use std::sync::Arc;
6
7type ProviderFactory = dyn Fn(&ModuleRef) -> Result<Arc<AnyProvider>> + Send + Sync;
8type AsyncProviderFactory =
9    dyn Fn(ModuleRef) -> BoxFuture<'static, Result<Arc<AnyProvider>>> + Send + Sync;
10type ProviderModuleInitHook = dyn Fn(Arc<AnyProvider>, &ModuleRef) -> Result<()> + Send + Sync;
11type ProviderApplicationHook =
12    dyn Fn(Arc<AnyProvider>, ModuleRef) -> BoxFuture<'static, Result<()>> + Send + Sync;
13
14/// Lifecycle hook for singleton providers that need synchronous module init work.
15pub trait ProviderOnModuleInit: Send + Sync + 'static {
16    fn on_module_init(&self, _module_ref: &ModuleRef) -> Result<()> {
17        Ok(())
18    }
19}
20
21/// Lifecycle hook for singleton providers that need async startup work.
22pub trait ProviderOnApplicationBootstrap: Send + Sync + 'static {
23    fn on_application_bootstrap(&self, _module_ref: ModuleRef) -> BoxFuture<'static, Result<()>> {
24        Box::pin(async { Ok(()) })
25    }
26}
27
28/// Lifecycle hook for singleton providers that need async shutdown cleanup.
29pub trait ProviderOnApplicationShutdown: Send + Sync + 'static {
30    fn on_application_shutdown(&self, _module_ref: ModuleRef) -> BoxFuture<'static, Result<()>> {
31        Box::pin(async { Ok(()) })
32    }
33}
34
35/// Builds an injectable value from the module provider graph.
36pub trait FromModuleRef: Sized + Send + Sync + 'static {
37    fn from_module_ref(module_ref: &ModuleRef) -> Result<Self>;
38}
39
40/// Lifetime strategy for provider resolution.
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum ProviderScope {
43    Singleton,
44    Request,
45    Transient,
46}
47
48/// A provider registration, similar to a Nest provider entry.
49#[derive(Clone)]
50pub struct ProviderDefinition {
51    token: ProviderToken,
52    factory: ProviderFactoryKind,
53    scope: ProviderScope,
54    lifecycle: ProviderLifecycleHooks,
55    alias_target: Option<ProviderToken>,
56}
57
58#[derive(Clone)]
59enum ProviderFactoryKind {
60    Sync(Arc<ProviderFactory>),
61    Async(Arc<AsyncProviderFactory>),
62}
63
64#[derive(Clone, Default)]
65pub(super) struct ProviderLifecycleHooks {
66    on_module_init: Option<Arc<ProviderModuleInitHook>>,
67    on_application_bootstrap: Option<Arc<ProviderApplicationHook>>,
68    on_application_shutdown: Option<Arc<ProviderApplicationHook>>,
69}
70
71impl ProviderLifecycleHooks {
72    pub(super) fn has_hooks(&self) -> bool {
73        self.on_module_init.is_some()
74            || self.on_application_bootstrap.is_some()
75            || self.on_application_shutdown.is_some()
76    }
77
78    pub(super) fn on_module_init(&self) -> Option<&Arc<ProviderModuleInitHook>> {
79        self.on_module_init.as_ref()
80    }
81
82    pub(super) fn on_application_bootstrap(&self) -> Option<&Arc<ProviderApplicationHook>> {
83        self.on_application_bootstrap.as_ref()
84    }
85
86    pub(super) fn on_application_shutdown(&self) -> Option<&Arc<ProviderApplicationHook>> {
87        self.on_application_shutdown.as_ref()
88    }
89}
90
91impl fmt::Debug for ProviderDefinition {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        f.debug_struct("ProviderDefinition")
94            .field("token", &self.token)
95            .field("scope", &self.scope)
96            .field("async", &self.factory.is_async())
97            .field("alias_target", &self.alias_target)
98            .finish_non_exhaustive()
99    }
100}
101
102impl ProviderFactoryKind {
103    fn is_async(&self) -> bool {
104        matches!(self, Self::Async(_))
105    }
106}
107
108impl ProviderDefinition {
109    pub fn singleton<T>(value: T) -> Self
110    where
111        T: Send + Sync + 'static,
112    {
113        Self::from_arc(Arc::new(value))
114    }
115
116    pub fn named_singleton<T>(token: impl Into<String>, value: T) -> Self
117    where
118        T: Send + Sync + 'static,
119    {
120        Self::named_from_arc(token, Arc::new(value))
121    }
122
123    pub fn from_arc<T>(value: Arc<T>) -> Self
124    where
125        T: Send + Sync + 'static,
126    {
127        let token = ProviderToken::of::<T>();
128        Self::named_from_arc(token.as_str(), value)
129    }
130
131    pub fn named_from_arc<T>(token: impl Into<String>, value: Arc<T>) -> Self
132    where
133        T: Send + Sync + 'static,
134    {
135        let token = ProviderToken::named(token);
136        let factory_value = Arc::clone(&value);
137        Self {
138            token,
139            factory: ProviderFactoryKind::Sync(Arc::new(move |_| {
140                Ok(Arc::clone(&factory_value) as Arc<AnyProvider>)
141            })),
142            scope: ProviderScope::Singleton,
143            lifecycle: ProviderLifecycleHooks::default(),
144            alias_target: None,
145        }
146    }
147
148    pub fn factory<T, F>(factory: F) -> Self
149    where
150        T: Send + Sync + 'static,
151        F: Fn(&ModuleRef) -> Result<T> + Send + Sync + 'static,
152    {
153        Self::named_factory(ProviderToken::of::<T>().as_str(), factory)
154    }
155
156    pub fn factory_arc<T, F>(factory: F) -> Self
157    where
158        T: Send + Sync + 'static,
159        F: Fn(&ModuleRef) -> Result<Arc<T>> + Send + Sync + 'static,
160    {
161        Self::named_factory_arc(ProviderToken::of::<T>().as_str(), factory)
162    }
163
164    pub fn named_factory<T, F>(token: impl Into<String>, factory: F) -> Self
165    where
166        T: Send + Sync + 'static,
167        F: Fn(&ModuleRef) -> Result<T> + Send + Sync + 'static,
168    {
169        Self {
170            token: ProviderToken::named(token),
171            factory: ProviderFactoryKind::Sync(Arc::new(move |module_ref| {
172                Ok(Arc::new(factory(module_ref)?) as Arc<AnyProvider>)
173            })),
174            scope: ProviderScope::Singleton,
175            lifecycle: ProviderLifecycleHooks::default(),
176            alias_target: None,
177        }
178    }
179
180    pub fn named_factory_arc<T, F>(token: impl Into<String>, factory: F) -> Self
181    where
182        T: Send + Sync + 'static,
183        F: Fn(&ModuleRef) -> Result<Arc<T>> + Send + Sync + 'static,
184    {
185        Self {
186            token: ProviderToken::named(token),
187            factory: ProviderFactoryKind::Sync(Arc::new(move |module_ref| {
188                Ok(factory(module_ref)? as Arc<AnyProvider>)
189            })),
190            scope: ProviderScope::Singleton,
191            lifecycle: ProviderLifecycleHooks::default(),
192            alias_target: None,
193        }
194    }
195
196    pub fn async_factory<T, F, Fut>(factory: F) -> Self
197    where
198        T: Send + Sync + 'static,
199        F: Fn(ModuleRef) -> Fut + Send + Sync + 'static,
200        Fut: Future<Output = Result<T>> + Send + 'static,
201    {
202        Self::named_async_factory(ProviderToken::of::<T>().as_str(), factory)
203    }
204
205    pub fn async_factory_arc<T, F, Fut>(factory: F) -> Self
206    where
207        T: Send + Sync + 'static,
208        F: Fn(ModuleRef) -> Fut + Send + Sync + 'static,
209        Fut: Future<Output = Result<Arc<T>>> + Send + 'static,
210    {
211        Self::named_async_factory_arc(ProviderToken::of::<T>().as_str(), factory)
212    }
213
214    pub fn named_async_factory<T, F, Fut>(token: impl Into<String>, factory: F) -> Self
215    where
216        T: Send + Sync + 'static,
217        F: Fn(ModuleRef) -> Fut + Send + Sync + 'static,
218        Fut: Future<Output = Result<T>> + Send + 'static,
219    {
220        Self {
221            token: ProviderToken::named(token),
222            factory: ProviderFactoryKind::Async(Arc::new(move |module_ref| {
223                let future = factory(module_ref);
224                Box::pin(async move { Ok(Arc::new(future.await?) as Arc<AnyProvider>) })
225            })),
226            scope: ProviderScope::Singleton,
227            lifecycle: ProviderLifecycleHooks::default(),
228            alias_target: None,
229        }
230    }
231
232    pub fn named_async_factory_arc<T, F, Fut>(token: impl Into<String>, factory: F) -> Self
233    where
234        T: Send + Sync + 'static,
235        F: Fn(ModuleRef) -> Fut + Send + Sync + 'static,
236        Fut: Future<Output = Result<Arc<T>>> + Send + 'static,
237    {
238        Self {
239            token: ProviderToken::named(token),
240            factory: ProviderFactoryKind::Async(Arc::new(move |module_ref| {
241                let future = factory(module_ref);
242                Box::pin(async move { Ok(future.await? as Arc<AnyProvider>) })
243            })),
244            scope: ProviderScope::Singleton,
245            lifecycle: ProviderLifecycleHooks::default(),
246            alias_target: None,
247        }
248    }
249
250    pub fn injectable<T>() -> Self
251    where
252        T: FromModuleRef,
253    {
254        Self::factory::<T, _>(T::from_module_ref)
255    }
256
257    pub fn named_injectable<T>(token: impl Into<String>) -> Self
258    where
259        T: FromModuleRef,
260    {
261        Self::named_factory::<T, _>(token, T::from_module_ref)
262    }
263
264    pub fn alias<T>(target: ProviderToken) -> Self
265    where
266        T: Send + Sync + 'static,
267    {
268        Self::named_alias(ProviderToken::of::<T>().as_str(), target)
269    }
270
271    pub fn named_alias(token: impl Into<String>, target: ProviderToken) -> Self {
272        Self {
273            token: ProviderToken::named(token),
274            factory: ProviderFactoryKind::Sync(Arc::new(|_| {
275                Err(BootError::Internal(
276                    "provider aliases must be resolved through ModuleRef".to_string(),
277                ))
278            })),
279            scope: ProviderScope::Singleton,
280            lifecycle: ProviderLifecycleHooks::default(),
281            alias_target: Some(target),
282        }
283    }
284
285    pub fn transient<T, F>(factory: F) -> Self
286    where
287        T: Send + Sync + 'static,
288        F: Fn(&ModuleRef) -> Result<T> + Send + Sync + 'static,
289    {
290        Self::factory::<T, _>(factory).with_scope(ProviderScope::Transient)
291    }
292
293    pub fn transient_arc<T, F>(factory: F) -> Self
294    where
295        T: Send + Sync + 'static,
296        F: Fn(&ModuleRef) -> Result<Arc<T>> + Send + Sync + 'static,
297    {
298        Self::factory_arc::<T, _>(factory).with_scope(ProviderScope::Transient)
299    }
300
301    pub fn named_transient<T, F>(token: impl Into<String>, factory: F) -> Self
302    where
303        T: Send + Sync + 'static,
304        F: Fn(&ModuleRef) -> Result<T> + Send + Sync + 'static,
305    {
306        Self::named_factory::<T, _>(token, factory).with_scope(ProviderScope::Transient)
307    }
308
309    pub fn named_transient_arc<T, F>(token: impl Into<String>, factory: F) -> Self
310    where
311        T: Send + Sync + 'static,
312        F: Fn(&ModuleRef) -> Result<Arc<T>> + Send + Sync + 'static,
313    {
314        Self::named_factory_arc::<T, _>(token, factory).with_scope(ProviderScope::Transient)
315    }
316
317    pub fn transient_injectable<T>() -> Self
318    where
319        T: FromModuleRef,
320    {
321        Self::injectable::<T>().with_scope(ProviderScope::Transient)
322    }
323
324    pub fn named_transient_injectable<T>(token: impl Into<String>) -> Self
325    where
326        T: FromModuleRef,
327    {
328        Self::named_injectable::<T>(token).with_scope(ProviderScope::Transient)
329    }
330
331    pub fn request_scoped<T, F>(factory: F) -> Self
332    where
333        T: Send + Sync + 'static,
334        F: Fn(&ModuleRef) -> Result<T> + Send + Sync + 'static,
335    {
336        Self::factory::<T, _>(factory).with_scope(ProviderScope::Request)
337    }
338
339    pub fn request_scoped_arc<T, F>(factory: F) -> Self
340    where
341        T: Send + Sync + 'static,
342        F: Fn(&ModuleRef) -> Result<Arc<T>> + Send + Sync + 'static,
343    {
344        Self::factory_arc::<T, _>(factory).with_scope(ProviderScope::Request)
345    }
346
347    pub fn named_request_scoped<T, F>(token: impl Into<String>, factory: F) -> Self
348    where
349        T: Send + Sync + 'static,
350        F: Fn(&ModuleRef) -> Result<T> + Send + Sync + 'static,
351    {
352        Self::named_factory::<T, _>(token, factory).with_scope(ProviderScope::Request)
353    }
354
355    pub fn named_request_scoped_arc<T, F>(token: impl Into<String>, factory: F) -> Self
356    where
357        T: Send + Sync + 'static,
358        F: Fn(&ModuleRef) -> Result<Arc<T>> + Send + Sync + 'static,
359    {
360        Self::named_factory_arc::<T, _>(token, factory).with_scope(ProviderScope::Request)
361    }
362
363    pub fn request_scoped_injectable<T>() -> Self
364    where
365        T: FromModuleRef,
366    {
367        Self::injectable::<T>().with_scope(ProviderScope::Request)
368    }
369
370    pub fn named_request_scoped_injectable<T>(token: impl Into<String>) -> Self
371    where
372        T: FromModuleRef,
373    {
374        Self::named_injectable::<T>(token).with_scope(ProviderScope::Request)
375    }
376
377    pub fn with_scope(mut self, scope: ProviderScope) -> Self {
378        self.scope = scope;
379        self
380    }
381
382    pub fn with_on_module_init<T>(mut self) -> Self
383    where
384        T: ProviderOnModuleInit,
385    {
386        self.lifecycle.on_module_init = Some(Arc::new(|provider, module_ref| {
387            let provider = downcast_lifecycle_provider::<T>(provider)?;
388            provider.on_module_init(module_ref)
389        }));
390        self
391    }
392
393    pub fn with_on_application_bootstrap<T>(mut self) -> Self
394    where
395        T: ProviderOnApplicationBootstrap,
396    {
397        self.lifecycle.on_application_bootstrap =
398            Some(Arc::new(
399                |provider, module_ref| match downcast_lifecycle_provider::<T>(provider) {
400                    Ok(provider) => provider.on_application_bootstrap(module_ref),
401                    Err(error) => Box::pin(async move { Err(error) }),
402                },
403            ));
404        self
405    }
406
407    pub fn with_on_application_shutdown<T>(mut self) -> Self
408    where
409        T: ProviderOnApplicationShutdown,
410    {
411        self.lifecycle.on_application_shutdown =
412            Some(Arc::new(
413                |provider, module_ref| match downcast_lifecycle_provider::<T>(provider) {
414                    Ok(provider) => provider.on_application_shutdown(module_ref),
415                    Err(error) => Box::pin(async move { Err(error) }),
416                },
417            ));
418        self
419    }
420
421    pub fn token(&self) -> &ProviderToken {
422        &self.token
423    }
424
425    pub fn scope(&self) -> ProviderScope {
426        self.scope
427    }
428
429    pub(super) fn is_alias(&self) -> bool {
430        self.alias_target.is_some()
431    }
432
433    pub(super) fn is_async_factory(&self) -> bool {
434        self.factory.is_async()
435    }
436
437    pub(super) fn alias_target(&self) -> Option<&ProviderToken> {
438        self.alias_target.as_ref()
439    }
440
441    pub(super) fn lifecycle(&self) -> &ProviderLifecycleHooks {
442        &self.lifecycle
443    }
444
445    pub(super) fn build(&self, module_ref: &ModuleRef) -> Result<Arc<AnyProvider>> {
446        match &self.factory {
447            ProviderFactoryKind::Sync(factory) => factory(module_ref),
448            ProviderFactoryKind::Async(_) => Err(BootError::Internal(format!(
449                "async provider factory requires async application build: {}",
450                self.token
451            ))),
452        }
453    }
454
455    pub(super) async fn build_async(&self, module_ref: ModuleRef) -> Result<Arc<AnyProvider>> {
456        match &self.factory {
457            ProviderFactoryKind::Sync(factory) => factory(&module_ref),
458            ProviderFactoryKind::Async(factory) => factory(module_ref).await,
459        }
460    }
461}
462
463fn downcast_lifecycle_provider<T>(provider: Arc<AnyProvider>) -> Result<Arc<T>>
464where
465    T: Send + Sync + 'static,
466{
467    Arc::downcast::<T>(provider)
468        .map_err(|_| BootError::ProviderTypeMismatch(std::any::type_name::<T>().to_string()))
469}