Skip to main content

blazingly_di/
lib.rs

1#![forbid(unsafe_code)]
2
3use blazingly_contract::OperationFailure;
4use core::any::{Any, TypeId};
5use core::fmt;
6use core::future::Future;
7use core::ops::Deref;
8use core::pin::Pin;
9use std::rc::Rc;
10
11/// A resolved typed dependency passed to a handler or provider.
12#[derive(Clone, Debug)]
13pub struct Depends<T> {
14    value: Rc<T>,
15}
16
17impl<T> Depends<T> {
18    #[doc(hidden)]
19    #[must_use]
20    pub const fn from_rc(value: Rc<T>) -> Self {
21        Self { value }
22    }
23
24    #[must_use]
25    pub fn into_inner(self) -> Rc<T> {
26        self.value
27    }
28}
29
30impl<T> Deref for Depends<T> {
31    type Target = T;
32
33    fn deref(&self) -> &Self::Target {
34        &self.value
35    }
36}
37
38/// The lifetime of a dependency provider.
39#[derive(Clone, Copy, Debug, Eq, PartialEq)]
40pub enum DependencyLifetime {
41    /// Created once while the executable application is compiled.
42    Singleton,
43    /// Created once for each operation invocation that needs it.
44    Request,
45    /// Created independently for every injection edge.
46    Transient,
47}
48
49/// Runtime identity of a typed dependency.
50#[derive(Clone, Copy, Eq, Hash, PartialEq)]
51pub struct DependencyKey {
52    type_id: TypeId,
53    type_name: &'static str,
54}
55
56impl DependencyKey {
57    #[must_use]
58    pub fn of<T: 'static>() -> Self {
59        Self {
60            type_id: TypeId::of::<T>(),
61            type_name: core::any::type_name::<T>(),
62        }
63    }
64
65    #[must_use]
66    pub const fn type_id(self) -> TypeId {
67        self.type_id
68    }
69
70    #[must_use]
71    pub const fn type_name(self) -> &'static str {
72        self.type_name
73    }
74}
75
76impl fmt::Debug for DependencyKey {
77    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
78        formatter
79            .debug_tuple("DependencyKey")
80            .field(&self.type_name)
81            .finish()
82    }
83}
84
85/// A dependency required by one operation handler.
86#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
87pub struct DependencyRequest {
88    key: DependencyKey,
89}
90
91impl DependencyRequest {
92    #[must_use]
93    pub fn of<T: 'static>() -> Self {
94        Self {
95            key: DependencyKey::of::<T>(),
96        }
97    }
98
99    #[must_use]
100    pub const fn key(self) -> DependencyKey {
101        self.key
102    }
103}
104
105/// A stable request rejection or an internal dependency failure.
106#[derive(Clone, Debug, PartialEq)]
107pub enum DependencyError {
108    Rejected(OperationFailure),
109    Internal { code: &'static str, message: String },
110}
111
112impl DependencyError {
113    #[must_use]
114    pub const fn rejected(failure: OperationFailure) -> Self {
115        Self::Rejected(failure)
116    }
117
118    #[must_use]
119    pub fn internal(code: &'static str, message: impl Into<String>) -> Self {
120        Self::Internal {
121            code,
122            message: message.into(),
123        }
124    }
125}
126
127impl fmt::Display for DependencyError {
128    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
129        match self {
130            Self::Rejected(failure) => write!(formatter, "{}", failure.message),
131            Self::Internal { message, .. } => formatter.write_str(message),
132        }
133    }
134}
135
136impl std::error::Error for DependencyError {}
137
138#[doc(hidden)]
139pub type DependencyValue = Rc<dyn Any>;
140
141/// A numeric source selected by the dependency compiler.
142#[doc(hidden)]
143#[derive(Clone, Copy, Debug, Eq, PartialEq)]
144pub enum DependencySlot {
145    Singleton(usize),
146    Request(usize),
147}
148
149/// A provider with all of its dependency slots compiled.
150#[doc(hidden)]
151#[derive(Clone)]
152pub struct CompiledProvider {
153    runner: ProviderRunner,
154    finalizer: Option<ProviderFinalizer>,
155}
156
157type SyncProviderRunner = Rc<
158    dyn Fn(
159        &[Option<DependencyValue>],
160        &[Option<DependencyValue>],
161    ) -> Result<DependencyValue, DependencyError>,
162>;
163type ProviderFuture =
164    Pin<Box<dyn Future<Output = Result<DependencyValue, DependencyError>> + 'static>>;
165type AsyncProviderRunner =
166    Rc<dyn Fn(&[Option<DependencyValue>], &[Option<DependencyValue>]) -> ProviderFuture>;
167
168#[derive(Clone)]
169enum ProviderRunner {
170    Sync(SyncProviderRunner),
171    Async(AsyncProviderRunner),
172}
173
174type SyncProviderFinalizer = Rc<dyn Fn(&DependencyValue) -> Result<(), DependencyError>>;
175type FinalizerFuture = Pin<Box<dyn Future<Output = Result<(), DependencyError>> + 'static>>;
176type AsyncProviderFinalizer = Rc<dyn Fn(&DependencyValue) -> FinalizerFuture>;
177
178#[derive(Clone)]
179enum ProviderFinalizer {
180    Sync(SyncProviderFinalizer),
181    Async(AsyncProviderFinalizer),
182}
183
184impl CompiledProvider {
185    #[doc(hidden)]
186    #[must_use]
187    pub fn run(
188        &self,
189        singletons: &[Option<DependencyValue>],
190        requests: &[Option<DependencyValue>],
191    ) -> ProviderFuture {
192        match &self.runner {
193            ProviderRunner::Sync(runner) => {
194                let result = runner(singletons, requests);
195                Box::pin(async move { result })
196            }
197            ProviderRunner::Async(runner) => runner(singletons, requests),
198        }
199    }
200
201    #[doc(hidden)]
202    pub fn run_sync(
203        &self,
204        singletons: &[Option<DependencyValue>],
205        requests: &[Option<DependencyValue>],
206    ) -> Result<DependencyValue, DependencyError> {
207        match &self.runner {
208            ProviderRunner::Sync(runner) => runner(singletons, requests),
209            ProviderRunner::Async(_) => Err(DependencyError::internal(
210                "async_singleton_provider",
211                "async providers cannot use the singleton lifetime",
212            )),
213        }
214    }
215
216    #[doc(hidden)]
217    pub fn finalize(&self, value: &DependencyValue) -> FinalizerFuture {
218        match &self.finalizer {
219            None => Box::pin(async { Ok(()) }),
220            Some(ProviderFinalizer::Sync(finalizer)) => {
221                let result = finalizer(value);
222                Box::pin(async move { result })
223            }
224            Some(ProviderFinalizer::Async(finalizer)) => finalizer(value),
225        }
226    }
227}
228
229/// Internal compilation error. A valid framework-generated plan never exposes
230/// this to request handling.
231#[doc(hidden)]
232#[derive(Clone, Copy, Debug, Eq, PartialEq)]
233pub struct ProviderCompileError {
234    expected: usize,
235    actual: usize,
236}
237
238impl ProviderCompileError {
239    const fn arity(expected: usize, actual: usize) -> Self {
240        Self { expected, actual }
241    }
242}
243
244impl fmt::Display for ProviderCompileError {
245    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
246        write!(
247            formatter,
248            "provider expected {} dependency slots but received {}",
249            self.expected, self.actual
250        )
251    }
252}
253
254impl std::error::Error for ProviderCompileError {}
255
256type ProviderCompiler =
257    Rc<dyn Fn(&[DependencySlot]) -> Result<CompiledProvider, ProviderCompileError>>;
258
259/// A typed dependency provider registered in a plugin scope.
260#[derive(Clone)]
261pub struct Provider {
262    key: DependencyKey,
263    lifetime: DependencyLifetime,
264    dependencies: Vec<DependencyKey>,
265    compiler: ProviderCompiler,
266    finalizer: Option<ProviderFinalizer>,
267}
268
269impl Provider {
270    /// Registers an already-created singleton value.
271    #[must_use]
272    pub fn value<T: 'static>(value: T) -> Self {
273        let value: DependencyValue = Rc::new(value);
274        let compiler = Rc::new(move |slots: &[DependencySlot]| {
275            if !slots.is_empty() {
276                return Err(ProviderCompileError::arity(0, slots.len()));
277            }
278            let value = Rc::clone(&value);
279            Ok(CompiledProvider {
280                runner: ProviderRunner::Sync(Rc::new(move |_, _| Ok(Rc::clone(&value)))),
281                finalizer: None,
282            })
283        });
284        Self {
285            key: DependencyKey::of::<T>(),
286            lifetime: DependencyLifetime::Singleton,
287            dependencies: Vec::new(),
288            compiler,
289            finalizer: None,
290        }
291    }
292
293    /// Registers an infallible singleton factory.
294    #[must_use]
295    pub fn singleton<Arguments, Output, Factory>(factory: Factory) -> Self
296    where
297        Factory: ProviderFactory<Arguments, Output>,
298        Output: 'static,
299    {
300        Self::from_factory(DependencyLifetime::Singleton, factory)
301    }
302
303    /// Registers an infallible request-scoped factory.
304    #[must_use]
305    pub fn request<Arguments, Output, Factory>(factory: Factory) -> Self
306    where
307        Factory: ProviderFactory<Arguments, Output>,
308        Output: 'static,
309    {
310        Self::from_factory(DependencyLifetime::Request, factory)
311    }
312
313    /// Registers an infallible transient factory.
314    #[must_use]
315    pub fn transient<Arguments, Output, Factory>(factory: Factory) -> Self
316    where
317        Factory: ProviderFactory<Arguments, Output>,
318        Output: 'static,
319    {
320        Self::from_factory(DependencyLifetime::Transient, factory)
321    }
322
323    /// Registers a fallible singleton factory.
324    #[must_use]
325    pub fn try_singleton<Arguments, Output, Factory>(factory: Factory) -> Self
326    where
327        Factory: FallibleProviderFactory<Arguments, Output>,
328        Output: 'static,
329    {
330        Self::from_fallible_factory(DependencyLifetime::Singleton, factory)
331    }
332
333    /// Registers a fallible request-scoped factory.
334    #[must_use]
335    pub fn try_request<Arguments, Output, Factory>(factory: Factory) -> Self
336    where
337        Factory: FallibleProviderFactory<Arguments, Output>,
338        Output: 'static,
339    {
340        Self::from_fallible_factory(DependencyLifetime::Request, factory)
341    }
342
343    /// Registers a fallible transient factory.
344    #[must_use]
345    pub fn try_transient<Arguments, Output, Factory>(factory: Factory) -> Self
346    where
347        Factory: FallibleProviderFactory<Arguments, Output>,
348        Output: 'static,
349    {
350        Self::from_fallible_factory(DependencyLifetime::Transient, factory)
351    }
352
353    /// Registers a request provider with a reverse-order finalizer.
354    #[must_use]
355    pub fn request_scoped<Arguments, Output, Factory, Finalizer>(
356        factory: Factory,
357        finalizer: Finalizer,
358    ) -> Self
359    where
360        Factory: ProviderFactory<Arguments, Output>,
361        Finalizer: Fn(Depends<Output>) + 'static,
362        Output: 'static,
363    {
364        let mut provider = Self::from_factory(DependencyLifetime::Request, factory);
365        provider.finalizer = Some(typed_finalizer(finalizer));
366        provider
367    }
368
369    /// Registers a fallible request provider with a reverse-order finalizer.
370    #[must_use]
371    pub fn try_request_scoped<Arguments, Output, Factory, Finalizer>(
372        factory: Factory,
373        finalizer: Finalizer,
374    ) -> Self
375    where
376        Factory: FallibleProviderFactory<Arguments, Output>,
377        Finalizer: Fn(Depends<Output>) + 'static,
378        Output: 'static,
379    {
380        let mut provider = Self::from_fallible_factory(DependencyLifetime::Request, factory);
381        provider.finalizer = Some(typed_finalizer(finalizer));
382        provider
383    }
384
385    /// Registers an async request-scoped factory.
386    #[must_use]
387    pub fn request_async<Arguments, Output, Factory, FactoryFuture>(factory: Factory) -> Self
388    where
389        Factory: AsyncProviderFactory<Arguments, Output, FactoryFuture>,
390        FactoryFuture: Future<Output = Output> + 'static,
391        Output: 'static,
392    {
393        Self::from_async_factory(DependencyLifetime::Request, factory)
394    }
395
396    /// Registers an async transient factory.
397    #[must_use]
398    pub fn transient_async<Arguments, Output, Factory, FactoryFuture>(factory: Factory) -> Self
399    where
400        Factory: AsyncProviderFactory<Arguments, Output, FactoryFuture>,
401        FactoryFuture: Future<Output = Output> + 'static,
402        Output: 'static,
403    {
404        Self::from_async_factory(DependencyLifetime::Transient, factory)
405    }
406
407    /// Registers a fallible async request-scoped factory.
408    #[must_use]
409    pub fn try_request_async<Arguments, Output, Factory, FactoryFuture>(factory: Factory) -> Self
410    where
411        Factory: FallibleAsyncProviderFactory<Arguments, Output, FactoryFuture>,
412        FactoryFuture: Future<Output = Result<Output, DependencyError>> + 'static,
413        Output: 'static,
414    {
415        Self::from_fallible_async_factory(DependencyLifetime::Request, factory)
416    }
417
418    /// Registers a fallible async transient factory.
419    #[must_use]
420    pub fn try_transient_async<Arguments, Output, Factory, FactoryFuture>(factory: Factory) -> Self
421    where
422        Factory: FallibleAsyncProviderFactory<Arguments, Output, FactoryFuture>,
423        FactoryFuture: Future<Output = Result<Output, DependencyError>> + 'static,
424        Output: 'static,
425    {
426        Self::from_fallible_async_factory(DependencyLifetime::Transient, factory)
427    }
428
429    /// Registers an async request provider with an async finalizer.
430    #[must_use]
431    pub fn request_async_scoped<
432        Arguments,
433        Output,
434        Factory,
435        FactoryFuture,
436        Finalizer,
437        FinalizerFuture,
438    >(
439        factory: Factory,
440        finalizer: Finalizer,
441    ) -> Self
442    where
443        Factory: AsyncProviderFactory<Arguments, Output, FactoryFuture>,
444        FactoryFuture: Future<Output = Output> + 'static,
445        Finalizer: Fn(Depends<Output>) -> FinalizerFuture + 'static,
446        FinalizerFuture: Future<Output = ()> + 'static,
447        Output: 'static,
448    {
449        let mut provider = Self::from_async_factory(DependencyLifetime::Request, factory);
450        provider.finalizer = Some(typed_async_finalizer(finalizer));
451        provider
452    }
453
454    /// Registers a fallible async request provider with an async finalizer.
455    #[must_use]
456    pub fn try_request_async_scoped<
457        Arguments,
458        Output,
459        Factory,
460        FactoryFuture,
461        Finalizer,
462        FinalizerFuture,
463    >(
464        factory: Factory,
465        finalizer: Finalizer,
466    ) -> Self
467    where
468        Factory: FallibleAsyncProviderFactory<Arguments, Output, FactoryFuture>,
469        FactoryFuture: Future<Output = Result<Output, DependencyError>> + 'static,
470        Finalizer: Fn(Depends<Output>) -> FinalizerFuture + 'static,
471        FinalizerFuture: Future<Output = ()> + 'static,
472        Output: 'static,
473    {
474        let mut provider = Self::from_fallible_async_factory(DependencyLifetime::Request, factory);
475        provider.finalizer = Some(typed_async_finalizer(finalizer));
476        provider
477    }
478
479    fn from_factory<Arguments, Output, Factory>(
480        lifetime: DependencyLifetime,
481        factory: Factory,
482    ) -> Self
483    where
484        Factory: ProviderFactory<Arguments, Output>,
485        Output: 'static,
486    {
487        let dependencies = Factory::dependency_keys();
488        let factory = Rc::new(factory);
489        let compiler =
490            Rc::new(move |slots: &[DependencySlot]| Factory::compile(Rc::clone(&factory), slots));
491        Self {
492            key: DependencyKey::of::<Output>(),
493            lifetime,
494            dependencies,
495            compiler,
496            finalizer: None,
497        }
498    }
499
500    fn from_fallible_factory<Arguments, Output, Factory>(
501        lifetime: DependencyLifetime,
502        factory: Factory,
503    ) -> Self
504    where
505        Factory: FallibleProviderFactory<Arguments, Output>,
506        Output: 'static,
507    {
508        let dependencies = Factory::dependency_keys();
509        let factory = Rc::new(factory);
510        let compiler =
511            Rc::new(move |slots: &[DependencySlot]| Factory::compile(Rc::clone(&factory), slots));
512        Self {
513            key: DependencyKey::of::<Output>(),
514            lifetime,
515            dependencies,
516            compiler,
517            finalizer: None,
518        }
519    }
520
521    fn from_async_factory<Arguments, Output, Factory, FactoryFuture>(
522        lifetime: DependencyLifetime,
523        factory: Factory,
524    ) -> Self
525    where
526        Factory: AsyncProviderFactory<Arguments, Output, FactoryFuture>,
527        FactoryFuture: Future<Output = Output> + 'static,
528        Output: 'static,
529    {
530        let dependencies = Factory::dependency_keys();
531        let factory = Rc::new(factory);
532        let compiler =
533            Rc::new(move |slots: &[DependencySlot]| Factory::compile(Rc::clone(&factory), slots));
534        Self {
535            key: DependencyKey::of::<Output>(),
536            lifetime,
537            dependencies,
538            compiler,
539            finalizer: None,
540        }
541    }
542
543    fn from_fallible_async_factory<Arguments, Output, Factory, FactoryFuture>(
544        lifetime: DependencyLifetime,
545        factory: Factory,
546    ) -> Self
547    where
548        Factory: FallibleAsyncProviderFactory<Arguments, Output, FactoryFuture>,
549        FactoryFuture: Future<Output = Result<Output, DependencyError>> + 'static,
550        Output: 'static,
551    {
552        let dependencies = Factory::dependency_keys();
553        let factory = Rc::new(factory);
554        let compiler =
555            Rc::new(move |slots: &[DependencySlot]| Factory::compile(Rc::clone(&factory), slots));
556        Self {
557            key: DependencyKey::of::<Output>(),
558            lifetime,
559            dependencies,
560            compiler,
561            finalizer: None,
562        }
563    }
564
565    #[must_use]
566    pub const fn key(&self) -> DependencyKey {
567        self.key
568    }
569
570    #[must_use]
571    pub const fn lifetime(&self) -> DependencyLifetime {
572        self.lifetime
573    }
574
575    #[must_use]
576    pub fn dependencies(&self) -> &[DependencyKey] {
577        &self.dependencies
578    }
579
580    #[doc(hidden)]
581    pub fn compile(
582        &self,
583        slots: &[DependencySlot],
584    ) -> Result<CompiledProvider, ProviderCompileError> {
585        let mut compiled = (self.compiler)(slots)?;
586        compiled.finalizer.clone_from(&self.finalizer);
587        Ok(compiled)
588    }
589}
590
591/// Converts an infallible typed closure into a provider factory.
592#[doc(hidden)]
593pub trait ProviderFactory<Arguments, Output>: 'static {
594    fn dependency_keys() -> Vec<DependencyKey>;
595
596    fn compile(
597        factory: Rc<Self>,
598        slots: &[DependencySlot],
599    ) -> Result<CompiledProvider, ProviderCompileError>;
600}
601
602/// Converts a fallible typed closure into a provider factory.
603#[doc(hidden)]
604pub trait FallibleProviderFactory<Arguments, Output>: 'static {
605    fn dependency_keys() -> Vec<DependencyKey>;
606
607    fn compile(
608        factory: Rc<Self>,
609        slots: &[DependencySlot],
610    ) -> Result<CompiledProvider, ProviderCompileError>;
611}
612
613/// Converts an async typed closure into a provider factory.
614#[doc(hidden)]
615pub trait AsyncProviderFactory<Arguments, Output, FactoryFuture>: 'static
616where
617    FactoryFuture: Future<Output = Output> + 'static,
618{
619    fn dependency_keys() -> Vec<DependencyKey>;
620
621    fn compile(
622        factory: Rc<Self>,
623        slots: &[DependencySlot],
624    ) -> Result<CompiledProvider, ProviderCompileError>;
625}
626
627/// Converts a fallible async typed closure into a provider factory.
628#[doc(hidden)]
629pub trait FallibleAsyncProviderFactory<Arguments, Output, FactoryFuture>: 'static
630where
631    FactoryFuture: Future<Output = Result<Output, DependencyError>> + 'static,
632{
633    fn dependency_keys() -> Vec<DependencyKey>;
634
635    fn compile(
636        factory: Rc<Self>,
637        slots: &[DependencySlot],
638    ) -> Result<CompiledProvider, ProviderCompileError>;
639}
640
641impl<Factory, Output> ProviderFactory<(), Output> for Factory
642where
643    Factory: Fn() -> Output + 'static,
644    Output: 'static,
645{
646    fn dependency_keys() -> Vec<DependencyKey> {
647        Vec::new()
648    }
649
650    fn compile(
651        factory: Rc<Self>,
652        slots: &[DependencySlot],
653    ) -> Result<CompiledProvider, ProviderCompileError> {
654        expect_arity(slots, 0)?;
655        Ok(CompiledProvider {
656            runner: ProviderRunner::Sync(Rc::new(move |_, _| {
657                Ok(Rc::new(factory()) as DependencyValue)
658            })),
659            finalizer: None,
660        })
661    }
662}
663
664impl<Factory, Output, FactoryFuture> AsyncProviderFactory<(), Output, FactoryFuture> for Factory
665where
666    Factory: Fn() -> FactoryFuture + 'static,
667    FactoryFuture: Future<Output = Output> + 'static,
668    Output: 'static,
669{
670    fn dependency_keys() -> Vec<DependencyKey> {
671        Vec::new()
672    }
673
674    fn compile(
675        factory: Rc<Self>,
676        slots: &[DependencySlot],
677    ) -> Result<CompiledProvider, ProviderCompileError> {
678        expect_arity(slots, 0)?;
679        Ok(CompiledProvider {
680            runner: ProviderRunner::Async(Rc::new(move |_, _| {
681                let future = factory();
682                Box::pin(async move { Ok(Rc::new(future.await) as DependencyValue) })
683            })),
684            finalizer: None,
685        })
686    }
687}
688
689impl<Factory, Output, FactoryFuture> FallibleAsyncProviderFactory<(), Output, FactoryFuture>
690    for Factory
691where
692    Factory: Fn() -> FactoryFuture + 'static,
693    FactoryFuture: Future<Output = Result<Output, DependencyError>> + 'static,
694    Output: 'static,
695{
696    fn dependency_keys() -> Vec<DependencyKey> {
697        Vec::new()
698    }
699
700    fn compile(
701        factory: Rc<Self>,
702        slots: &[DependencySlot],
703    ) -> Result<CompiledProvider, ProviderCompileError> {
704        expect_arity(slots, 0)?;
705        Ok(CompiledProvider {
706            runner: ProviderRunner::Async(Rc::new(move |_, _| {
707                let future = factory();
708                Box::pin(async move { future.await.map(|value| Rc::new(value) as DependencyValue) })
709            })),
710            finalizer: None,
711        })
712    }
713}
714
715impl<Factory, Output> FallibleProviderFactory<(), Output> for Factory
716where
717    Factory: Fn() -> Result<Output, DependencyError> + 'static,
718    Output: 'static,
719{
720    fn dependency_keys() -> Vec<DependencyKey> {
721        Vec::new()
722    }
723
724    fn compile(
725        factory: Rc<Self>,
726        slots: &[DependencySlot],
727    ) -> Result<CompiledProvider, ProviderCompileError> {
728        expect_arity(slots, 0)?;
729        Ok(CompiledProvider {
730            runner: ProviderRunner::Sync(Rc::new(move |_, _| {
731                factory().map(|value| Rc::new(value) as DependencyValue)
732            })),
733            finalizer: None,
734        })
735    }
736}
737
738macro_rules! provider_factory {
739    ($(($argument:ident, $value:ident, $index:tt)),+ $(,)?) => {
740        impl<Factory, Output, $($argument),+> ProviderFactory<($($argument,)+), Output> for Factory
741        where
742            Factory: Fn($(Depends<$argument>),+) -> Output + 'static,
743            Output: 'static,
744            $($argument: 'static),+
745        {
746            fn dependency_keys() -> Vec<DependencyKey> {
747                vec![$(DependencyKey::of::<$argument>()),+]
748            }
749
750            fn compile(
751                factory: Rc<Self>,
752                slots: &[DependencySlot],
753            ) -> Result<CompiledProvider, ProviderCompileError> {
754                expect_arity(slots, provider_factory!(@count $($argument),+))?;
755                $(let $value = slots[$index];)+
756                Ok(CompiledProvider {
757                    runner: ProviderRunner::Sync(Rc::new(move |singletons, requests| {
758                        $(
759                            let $value =
760                                resolve::<$argument>($value, singletons, requests)?;
761                        )+
762                        Ok(Rc::new(factory($($value),+)) as DependencyValue)
763                    })),
764                    finalizer: None,
765                })
766            }
767        }
768
769        impl<Factory, Output, $($argument),+>
770            FallibleProviderFactory<($($argument,)+), Output> for Factory
771        where
772            Factory: Fn($(Depends<$argument>),+) -> Result<Output, DependencyError> + 'static,
773            Output: 'static,
774            $($argument: 'static),+
775        {
776            fn dependency_keys() -> Vec<DependencyKey> {
777                vec![$(DependencyKey::of::<$argument>()),+]
778            }
779
780            fn compile(
781                factory: Rc<Self>,
782                slots: &[DependencySlot],
783            ) -> Result<CompiledProvider, ProviderCompileError> {
784                expect_arity(slots, provider_factory!(@count $($argument),+))?;
785                $(let $value = slots[$index];)+
786                Ok(CompiledProvider {
787                    runner: ProviderRunner::Sync(Rc::new(move |singletons, requests| {
788                        $(
789                            let $value =
790                                resolve::<$argument>($value, singletons, requests)?;
791                        )+
792                        factory($($value),+)
793                            .map(|value| Rc::new(value) as DependencyValue)
794                    })),
795                    finalizer: None,
796                })
797            }
798        }
799
800        impl<Factory, Output, FactoryFuture, $($argument),+>
801            AsyncProviderFactory<($($argument,)+), Output, FactoryFuture> for Factory
802        where
803            Factory: Fn($(Depends<$argument>),+) -> FactoryFuture + 'static,
804            FactoryFuture: Future<Output = Output> + 'static,
805            Output: 'static,
806            $($argument: 'static),+
807        {
808            fn dependency_keys() -> Vec<DependencyKey> {
809                vec![$(DependencyKey::of::<$argument>()),+]
810            }
811
812            fn compile(
813                factory: Rc<Self>,
814                slots: &[DependencySlot],
815            ) -> Result<CompiledProvider, ProviderCompileError> {
816                expect_arity(slots, provider_factory!(@count $($argument),+))?;
817                $(let $value = slots[$index];)+
818                Ok(CompiledProvider {
819                    runner: ProviderRunner::Async(Rc::new(move |singletons, requests| {
820                        $(
821                            let $value = match
822                                resolve::<$argument>($value, singletons, requests)
823                            {
824                                Ok(value) => value,
825                                Err(error) => return failed_provider_future(error),
826                            };
827                        )+
828                        let future = factory($($value),+);
829                        Box::pin(async move {
830                            Ok(Rc::new(future.await) as DependencyValue)
831                        })
832                    })),
833                    finalizer: None,
834                })
835            }
836        }
837
838        impl<Factory, Output, FactoryFuture, $($argument),+>
839            FallibleAsyncProviderFactory<($($argument,)+), Output, FactoryFuture> for Factory
840        where
841            Factory: Fn($(Depends<$argument>),+) -> FactoryFuture + 'static,
842            FactoryFuture: Future<Output = Result<Output, DependencyError>> + 'static,
843            Output: 'static,
844            $($argument: 'static),+
845        {
846            fn dependency_keys() -> Vec<DependencyKey> {
847                vec![$(DependencyKey::of::<$argument>()),+]
848            }
849
850            fn compile(
851                factory: Rc<Self>,
852                slots: &[DependencySlot],
853            ) -> Result<CompiledProvider, ProviderCompileError> {
854                expect_arity(slots, provider_factory!(@count $($argument),+))?;
855                $(let $value = slots[$index];)+
856                Ok(CompiledProvider {
857                    runner: ProviderRunner::Async(Rc::new(move |singletons, requests| {
858                        $(
859                            let $value = match
860                                resolve::<$argument>($value, singletons, requests)
861                            {
862                                Ok(value) => value,
863                                Err(error) => return failed_provider_future(error),
864                            };
865                        )+
866                        let future = factory($($value),+);
867                        Box::pin(async move {
868                            future
869                                .await
870                                .map(|value| Rc::new(value) as DependencyValue)
871                        })
872                    })),
873                    finalizer: None,
874                })
875            }
876        }
877    };
878    (@count $($argument:ident),+) => {
879        <[()]>::len(&[$(provider_factory!(@unit $argument)),+])
880    };
881    (@unit $argument:ident) => { () };
882}
883
884provider_factory!((A, a, 0));
885provider_factory!((A, a, 0), (B, b, 1));
886provider_factory!((A, a, 0), (B, b, 1), (C, c, 2));
887provider_factory!((A, a, 0), (B, b, 1), (C, c, 2), (D, d, 3));
888provider_factory!((A, a, 0), (B, b, 1), (C, c, 2), (D, d, 3), (E, e, 4));
889provider_factory!(
890    (A, a, 0),
891    (B, b, 1),
892    (C, c, 2),
893    (D, d, 3),
894    (E, e, 4),
895    (F, f, 5)
896);
897provider_factory!(
898    (A, a, 0),
899    (B, b, 1),
900    (C, c, 2),
901    (D, d, 3),
902    (E, e, 4),
903    (F, f, 5),
904    (G, g, 6)
905);
906provider_factory!(
907    (A, a, 0),
908    (B, b, 1),
909    (C, c, 2),
910    (D, d, 3),
911    (E, e, 4),
912    (F, f, 5),
913    (G, g, 6),
914    (H, h, 7)
915);
916
917fn typed_finalizer<T, Finalizer>(finalizer: Finalizer) -> ProviderFinalizer
918where
919    Finalizer: Fn(Depends<T>) + 'static,
920    T: 'static,
921{
922    ProviderFinalizer::Sync(Rc::new(move |value| {
923        let dependency = Rc::clone(value).downcast::<T>().map_err(|_| {
924            DependencyError::internal(
925                "dependency_type_mismatch",
926                "compiled finalizer received an unexpected dependency type",
927            )
928        })?;
929        finalizer(Depends::from_rc(dependency));
930        Ok(())
931    }))
932}
933
934fn typed_async_finalizer<T, Finalizer, FinalizerOutput>(finalizer: Finalizer) -> ProviderFinalizer
935where
936    Finalizer: Fn(Depends<T>) -> FinalizerOutput + 'static,
937    FinalizerOutput: Future<Output = ()> + 'static,
938    T: 'static,
939{
940    ProviderFinalizer::Async(Rc::new(move |value| {
941        let Ok(dependency) = Rc::clone(value).downcast::<T>() else {
942            return Box::pin(async {
943                Err(DependencyError::internal(
944                    "dependency_type_mismatch",
945                    "compiled async finalizer received an unexpected dependency type",
946                ))
947            });
948        };
949        let future = finalizer(Depends::from_rc(dependency));
950        Box::pin(async move {
951            future.await;
952            Ok(())
953        })
954    }))
955}
956
957fn failed_provider_future(error: DependencyError) -> ProviderFuture {
958    Box::pin(async move { Err(error) })
959}
960
961fn expect_arity(slots: &[DependencySlot], expected: usize) -> Result<(), ProviderCompileError> {
962    if slots.len() == expected {
963        Ok(())
964    } else {
965        Err(ProviderCompileError::arity(expected, slots.len()))
966    }
967}
968
969/// Typed access to one provider invocation's compiled slots.
970///
971/// The macro-generated body of a request-aware provider reads its `Depends`
972/// arguments and its decoded request inputs through this reader, so the
973/// container stays a plain slice of erased values and never learns what an
974/// extractor is. Positions mirror the declaration: dependencies first, in
975/// argument order, then request inputs.
976#[doc(hidden)]
977pub struct SlotReader<'values> {
978    singletons: &'values [Option<DependencyValue>],
979    requests: &'values [Option<DependencyValue>],
980    slots: &'values [DependencySlot],
981    depends_count: usize,
982}
983
984impl SlotReader<'_> {
985    /// The `position`-th declared `Depends<T>` argument.
986    ///
987    /// # Errors
988    ///
989    /// Returns an internal dependency error when the compiled plan and the
990    /// generated code disagree, which a framework-generated pair never does.
991    pub fn depends<T: 'static>(&self, position: usize) -> Result<Depends<T>, DependencyError> {
992        let slot = self.slots.get(position).copied().ok_or_else(|| {
993            DependencyError::internal(
994                "invalid_dependency_slot",
995                "compiled provider requested an undeclared dependency position",
996            )
997        })?;
998        resolve(slot, self.singletons, self.requests)
999    }
1000
1001    /// The `position`-th declared request input, decoded before the provider
1002    /// chain ran.
1003    ///
1004    /// # Errors
1005    ///
1006    /// Returns an internal dependency error when the compiled plan and the
1007    /// generated code disagree, which a framework-generated pair never does.
1008    pub fn input<T: 'static>(&self, position: usize) -> Result<Rc<T>, DependencyError> {
1009        let slot = self
1010            .slots
1011            .get(self.depends_count + position)
1012            .copied()
1013            .ok_or_else(|| {
1014                DependencyError::internal(
1015                    "invalid_dependency_slot",
1016                    "compiled provider requested an undeclared input position",
1017                )
1018            })?;
1019        let value = match slot {
1020            DependencySlot::Singleton(index) => self.singletons.get(index),
1021            DependencySlot::Request(index) => self.requests.get(index),
1022        }
1023        .and_then(Option::as_ref)
1024        .ok_or_else(|| {
1025            DependencyError::internal(
1026                "invalid_dependency_slot",
1027                "compiled request input slot was not initialized",
1028            )
1029        })?;
1030        Rc::clone(value).downcast::<T>().map_err(|_| {
1031            DependencyError::internal(
1032                "dependency_type_mismatch",
1033                "compiled request input slot contained an unexpected type",
1034            )
1035        })
1036    }
1037}
1038
1039/// A synchronous slot-based provider body, generated by `#[provider]`.
1040#[doc(hidden)]
1041pub type SlotFactory<T> = Rc<dyn Fn(&SlotReader<'_>) -> Result<T, DependencyError>>;
1042
1043/// An asynchronous slot-based provider body, generated by `#[provider]`.
1044///
1045/// The closure reads everything it needs through the reader before returning
1046/// the future, so the future owns its captures and outlives the borrow.
1047#[doc(hidden)]
1048pub type AsyncSlotFactory<T> = Rc<
1049    dyn Fn(&SlotReader<'_>) -> Pin<Box<dyn Future<Output = Result<T, DependencyError>> + 'static>>,
1050>;
1051
1052impl Provider {
1053    fn from_slots_inner<T: 'static>(
1054        lifetime: DependencyLifetime,
1055        dependencies: Vec<DependencyKey>,
1056        input_count: usize,
1057        runner_of: impl Fn(Rc<Vec<DependencySlot>>, usize) -> ProviderRunner + 'static,
1058    ) -> Self {
1059        let depends_count = dependencies.len();
1060        let expected = depends_count + input_count;
1061        let compiler = Rc::new(move |slots: &[DependencySlot]| {
1062            expect_arity(slots, expected)?;
1063            let slots = Rc::new(slots.to_vec());
1064            Ok(CompiledProvider {
1065                runner: runner_of(slots, depends_count),
1066                finalizer: None,
1067            })
1068        });
1069        Self {
1070            key: DependencyKey::of::<T>(),
1071            lifetime,
1072            dependencies,
1073            compiler,
1074            finalizer: None,
1075        }
1076    }
1077
1078    /// A request-lifetime provider over compiled slots: dependencies first,
1079    /// then decoded request inputs. Macro plumbing, not application API.
1080    #[doc(hidden)]
1081    #[must_use]
1082    pub fn request_from_slots<T: 'static>(
1083        dependencies: Vec<DependencyKey>,
1084        input_count: usize,
1085        factory: SlotFactory<T>,
1086    ) -> Self {
1087        Self::slot_provider::<T>(
1088            DependencyLifetime::Request,
1089            dependencies,
1090            input_count,
1091            factory,
1092        )
1093    }
1094
1095    /// The transient twin of [`Provider::request_from_slots`].
1096    #[doc(hidden)]
1097    #[must_use]
1098    pub fn transient_from_slots<T: 'static>(
1099        dependencies: Vec<DependencyKey>,
1100        input_count: usize,
1101        factory: SlotFactory<T>,
1102    ) -> Self {
1103        Self::slot_provider::<T>(
1104            DependencyLifetime::Transient,
1105            dependencies,
1106            input_count,
1107            factory,
1108        )
1109    }
1110
1111    /// The asynchronous twin of [`Provider::request_from_slots`].
1112    #[doc(hidden)]
1113    #[must_use]
1114    pub fn request_from_slots_async<T: 'static>(
1115        dependencies: Vec<DependencyKey>,
1116        input_count: usize,
1117        factory: AsyncSlotFactory<T>,
1118    ) -> Self {
1119        Self::async_slot_provider::<T>(
1120            DependencyLifetime::Request,
1121            dependencies,
1122            input_count,
1123            factory,
1124        )
1125    }
1126
1127    /// The asynchronous transient twin of [`Provider::request_from_slots`].
1128    #[doc(hidden)]
1129    #[must_use]
1130    pub fn transient_from_slots_async<T: 'static>(
1131        dependencies: Vec<DependencyKey>,
1132        input_count: usize,
1133        factory: AsyncSlotFactory<T>,
1134    ) -> Self {
1135        Self::async_slot_provider::<T>(
1136            DependencyLifetime::Transient,
1137            dependencies,
1138            input_count,
1139            factory,
1140        )
1141    }
1142
1143    fn slot_provider<T: 'static>(
1144        lifetime: DependencyLifetime,
1145        dependencies: Vec<DependencyKey>,
1146        input_count: usize,
1147        factory: SlotFactory<T>,
1148    ) -> Self {
1149        Self::from_slots_inner::<T>(
1150            lifetime,
1151            dependencies,
1152            input_count,
1153            move |slots, depends| {
1154                let factory = Rc::clone(&factory);
1155                ProviderRunner::Sync(Rc::new(move |singletons, requests| {
1156                    let reader = SlotReader {
1157                        singletons,
1158                        requests,
1159                        slots: &slots,
1160                        depends_count: depends,
1161                    };
1162                    factory(&reader).map(|value| Rc::new(value) as DependencyValue)
1163                }))
1164            },
1165        )
1166    }
1167
1168    fn async_slot_provider<T: 'static>(
1169        lifetime: DependencyLifetime,
1170        dependencies: Vec<DependencyKey>,
1171        input_count: usize,
1172        factory: AsyncSlotFactory<T>,
1173    ) -> Self {
1174        Self::from_slots_inner::<T>(
1175            lifetime,
1176            dependencies,
1177            input_count,
1178            move |slots, depends| {
1179                let factory = Rc::clone(&factory);
1180                ProviderRunner::Async(Rc::new(move |singletons, requests| {
1181                    let reader = SlotReader {
1182                        singletons,
1183                        requests,
1184                        slots: &slots,
1185                        depends_count: depends,
1186                    };
1187                    let future = factory(&reader);
1188                    Box::pin(
1189                        async move { future.await.map(|value| Rc::new(value) as DependencyValue) },
1190                    )
1191                }))
1192            },
1193        )
1194    }
1195}
1196
1197fn resolve<T: 'static>(
1198    slot: DependencySlot,
1199    singletons: &[Option<DependencyValue>],
1200    requests: &[Option<DependencyValue>],
1201) -> Result<Depends<T>, DependencyError> {
1202    let value = match slot {
1203        DependencySlot::Singleton(index) => singletons.get(index),
1204        DependencySlot::Request(index) => requests.get(index),
1205    }
1206    .and_then(Option::as_ref)
1207    .ok_or_else(|| {
1208        DependencyError::internal(
1209            "invalid_dependency_slot",
1210            "compiled dependency slot was not initialized",
1211        )
1212    })?;
1213    Rc::clone(value)
1214        .downcast::<T>()
1215        .map(Depends::from_rc)
1216        .map_err(|_| {
1217            DependencyError::internal(
1218                "dependency_type_mismatch",
1219                "compiled dependency slot contained an unexpected type",
1220            )
1221        })
1222}