Skip to main content

blazingly_di/
lib.rs

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