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