Skip to main content

ferroui_dependency_injection/
service_provider.rs

1//! `ServiceProvider.cs`, `ServiceProviderOptions.cs`, `IServiceScope.cs`, `IServiceScopeFactory.cs`,
2//! `ISupportRequiredService.cs`, `AsyncServiceScope.cs`, `ServiceProviderServiceExtensions.cs`,
3//! `ServiceProviderKeyedServiceExtensions.cs`, `ServiceLookup/CallSiteFactory.cs`,
4//! `ServiceLookup/CallSiteChain.cs`, `ServiceLookup/CallSiteValidator.cs`,
5//! `ServiceLookup/CallSiteRuntimeResolver.cs` and `ServiceLookup/ServiceProviderEngineScope.cs`.
6
7use crate::*;
8use ferroui_system::{threading::tasks::Task, IDisposable};
9use std::cell::{Cell, RefCell};
10use std::collections::{HashMap, HashSet};
11use std::rc::{Rc, Weak};
12
13#[derive(Clone, Copy, Debug, Default)]
14pub struct ServiceProviderOptions {
15    pub validate_scopes: bool,
16    pub validate_on_build: bool,
17}
18
19pub trait IServiceProvider {
20    fn ensure_active(&self) -> ServiceResult<()> {
21        Ok(())
22    }
23    /// Explicit capability query replaces CLR interface casts for alternate providers.
24    fn required_service_provider(&self) -> Option<&dyn ISupportRequiredService> {
25        None
26    }
27    fn disposable(&self) -> Option<&dyn IDisposable> {
28        None
29    }
30    fn required_service_erased(&self, id: &ServiceIdentifier) -> ServiceResult<ErasedService> {
31        if let Some(provider) = self.required_service_provider() {
32            provider.get_required_service_erased(id)
33        } else {
34            self.get_service_erased(id)?
35                .ok_or_else(|| ServiceError::MissingService(id.clone()))
36        }
37    }
38    fn get_service_erased(&self, id: &ServiceIdentifier) -> ServiceResult<Option<ErasedService>>;
39    fn get_services_erased(&self, id: &ServiceIdentifier) -> ServiceResult<Vec<ErasedService>>;
40}
41/// Required-service resolution for alternate providers (`ISupportRequiredService.cs`).
42pub trait ISupportRequiredService: IServiceProvider {
43    fn get_required_service_erased(&self, id: &ServiceIdentifier) -> ServiceResult<ErasedService> {
44        self.get_service_erased(id)?
45            .ok_or_else(|| ServiceError::MissingService(id.clone()))
46    }
47}
48impl ISupportRequiredService for ServiceProvider {}
49pub trait IKeyedServiceProvider: IServiceProvider {
50    fn get_keyed_service_erased(
51        &self,
52        service_type: ServiceType,
53        key: Option<ServiceKey>,
54    ) -> ServiceResult<Option<ErasedService>> {
55        self.get_service_erased(&ServiceIdentifier {
56            service_type,
57            service_key: key,
58        })
59    }
60    fn get_required_keyed_service_erased(
61        &self,
62        service_type: ServiceType,
63        key: Option<ServiceKey>,
64    ) -> ServiceResult<ErasedService> {
65        let id = ServiceIdentifier {
66            service_type,
67            service_key: key,
68        };
69        self.get_service_erased(&id)?
70            .ok_or(ServiceError::MissingService(id))
71    }
72}
73pub trait IServiceProviderIsService {
74    fn is_service(&self, service_type: ServiceType) -> bool;
75}
76pub trait IServiceProviderIsKeyedService {
77    fn is_keyed_service(&self, service_type: ServiceType, key: Option<&ServiceKey>) -> bool;
78}
79pub trait IServiceScopeFactory {
80    fn create_scope(&self) -> ServiceResult<ServiceScope>;
81    fn create_async_scope(&self) -> ServiceResult<AsyncServiceScope> {
82        Ok(AsyncServiceScope::new(Rc::new(self.create_scope()?)))
83    }
84}
85pub trait IServiceScope: IDisposable {
86    fn service_provider(&self) -> &ServiceProvider;
87    fn async_disposable(&self) -> Option<&dyn IAsyncDisposable> {
88        None
89    }
90    fn dispose_scope(&self) -> ServiceResult<()> {
91        self.dispose();
92        Ok(())
93    }
94}
95
96pub trait ServiceProviderServiceExtensions: IServiceProvider {
97    fn create_scope(&self) -> ServiceResult<ServiceScope> {
98        self.get_required_service::<dyn IServiceScopeFactory>()?
99            .create_scope()
100    }
101    fn create_async_scope(&self) -> ServiceResult<AsyncServiceScope> {
102        Ok(AsyncServiceScope::new(Rc::new(self.create_scope()?)))
103    }
104    fn get_service<S: ?Sized + 'static>(&self) -> ServiceResult<Option<Rc<S>>> {
105        self.get_keyed_service::<S>(None)
106    }
107    fn get_required_service<S: ?Sized + 'static>(&self) -> ServiceResult<Rc<S>> {
108        let value = self.required_service_erased(&ServiceIdentifier::of::<S>())?;
109        unerase_service(&value).ok_or(ServiceError::TypeMismatch(ServiceType::of::<S>()))
110    }
111    fn get_services<S: ?Sized + 'static>(&self) -> ServiceResult<Vec<Rc<S>>> {
112        self.get_keyed_services::<S>(None)
113    }
114    fn get_keyed_service<S: ?Sized + 'static>(
115        &self,
116        key: Option<&ServiceKey>,
117    ) -> ServiceResult<Option<Rc<S>>> {
118        self.get_service_erased(&ServiceIdentifier::keyed::<S>(key.cloned()))?
119            .map(|v| unerase_service(&v).ok_or(ServiceError::TypeMismatch(ServiceType::of::<S>())))
120            .transpose()
121    }
122    fn get_required_keyed_service<S: ?Sized + 'static>(
123        &self,
124        key: Option<&ServiceKey>,
125    ) -> ServiceResult<Rc<S>> {
126        self.get_keyed_service::<S>(key)?.ok_or_else(|| {
127            ServiceError::MissingService(ServiceIdentifier::keyed::<S>(key.cloned()))
128        })
129    }
130    fn get_keyed_services<S: ?Sized + 'static>(
131        &self,
132        key: Option<&ServiceKey>,
133    ) -> ServiceResult<Vec<Rc<S>>> {
134        self.get_services_erased(&ServiceIdentifier::keyed::<S>(key.cloned()))?
135            .iter()
136            .map(|v| unerase_service(v).ok_or(ServiceError::TypeMismatch(ServiceType::of::<S>())))
137            .collect()
138    }
139}
140impl<T: IServiceProvider + ?Sized> ServiceProviderServiceExtensions for T {}
141
142#[derive(Clone, Debug, PartialEq, Eq, Hash)]
143struct CacheKey {
144    index: usize,
145    key: Option<ServiceKey>,
146}
147struct Captured {
148    value: ErasedService,
149    dispose: Option<crate::service_descriptor::Dispose>,
150    dispose_async: Option<crate::service_descriptor::DisposeAsync>,
151}
152#[derive(Default)]
153struct ScopeState {
154    disposed: Cell<bool>,
155    provider: RefCell<Option<Weak<dyn IServiceProvider>>>,
156    keyed_provider: RefCell<Option<Weak<dyn IKeyedServiceProvider>>>,
157    scope_factory: RefCell<Option<Weak<dyn IServiceScopeFactory>>>,
158    is_service: RefCell<Option<Weak<dyn IServiceProviderIsService>>>,
159    is_keyed_service: RefCell<Option<Weak<dyn IServiceProviderIsKeyedService>>>,
160    cache: RefCell<HashMap<CacheKey, ErasedService>>,
161    disposables: RefCell<Vec<Captured>>,
162}
163/// How many validated resolutions a provider remembers before it starts again.
164const MAX_VALIDATED: usize = 4096;
165struct Root {
166    descriptors: Vec<ServiceDescriptor>,
167    lookup: HashMap<ServiceIdentifier, Vec<usize>>,
168    options: ServiceProviderOptions,
169    scope: Rc<ScopeState>,
170    resolving: RefCell<Vec<(CacheKey, usize)>>,
171    validated: RefCell<HashSet<(CacheKey, bool)>>,
172}
173/// Cloneable handle to a root provider or a scope. Neither Send nor Sync: services stay on the
174/// creating thread. Cloning shares state; it never creates a new scope.
175#[derive(Clone)]
176pub struct ServiceProvider {
177    root: Rc<Root>,
178    scope: Rc<ScopeState>,
179}
180#[derive(Clone)]
181pub struct ServiceScope {
182    provider: ServiceProvider,
183}
184/// Wraps any synchronous scope, preferring its async-disposal capability when available.
185#[derive(Clone)]
186pub struct AsyncServiceScope {
187    scope: Rc<dyn IServiceScope>,
188}
189impl AsyncServiceScope {
190    pub fn new(scope: Rc<dyn IServiceScope>) -> Self {
191        Self { scope }
192    }
193    pub fn service_provider(&self) -> &ServiceProvider {
194        self.scope.service_provider()
195    }
196    pub fn dispose(&self) -> ServiceResult<()> {
197        self.scope.dispose_scope()
198    }
199    pub fn dispose_async(&self) -> Task<()> {
200        if let Some(scope) = self.scope.async_disposable() {
201            scope.dispose_async()
202        } else {
203            match self.scope.dispose_scope() {
204                Ok(()) => Task::from_result(()),
205                Err(error) => Task::from_exception(Rc::new(error)),
206            }
207        }
208    }
209}
210impl IDisposable for AsyncServiceScope {
211    fn dispose(&self) {
212        self.dispose().unwrap_or_else(|e| panic!("{e}"));
213    }
214}
215impl IAsyncDisposable for AsyncServiceScope {
216    fn dispose_async(&self) -> Task<()> {
217        self.dispose_async()
218    }
219}
220impl IServiceScope for AsyncServiceScope {
221    fn service_provider(&self) -> &ServiceProvider {
222        self.service_provider()
223    }
224    fn async_disposable(&self) -> Option<&dyn IAsyncDisposable> {
225        Some(self)
226    }
227    fn dispose_scope(&self) -> ServiceResult<()> {
228        self.dispose()
229    }
230}
231impl IServiceScope for ServiceScope {
232    fn async_disposable(&self) -> Option<&dyn IAsyncDisposable> {
233        Some(self)
234    }
235    fn dispose_scope(&self) -> ServiceResult<()> {
236        self.dispose()
237    }
238    fn service_provider(&self) -> &ServiceProvider {
239        &self.provider
240    }
241}
242impl ServiceScope {
243    pub fn service_provider(&self) -> &ServiceProvider {
244        &self.provider
245    }
246    pub fn dispose(&self) -> ServiceResult<()> {
247        self.provider.dispose()
248    }
249    pub fn dispose_async(&self) -> Task<()> {
250        self.provider.dispose_async()
251    }
252}
253impl IDisposable for ServiceScope {
254    fn dispose(&self) {
255        self.dispose().unwrap_or_else(|e| panic!("{e}"));
256    }
257}
258impl IAsyncDisposable for ServiceScope {
259    fn dispose_async(&self) -> Task<()> {
260        self.dispose_async()
261    }
262}
263
264struct ResolutionGuard(Rc<Root>);
265impl Drop for ResolutionGuard {
266    fn drop(&mut self) {
267        self.0.resolving.borrow_mut().pop();
268    }
269}
270
271impl ServiceProvider {
272    pub(crate) fn build(
273        descriptors: Vec<ServiceDescriptor>,
274        options: ServiceProviderOptions,
275    ) -> ServiceResult<Self> {
276        let scope = Rc::new(ScopeState::default());
277        let mut lookup: HashMap<_, Vec<_>> = HashMap::new();
278        for (i, descriptor) in descriptors.iter().enumerate() {
279            lookup
280                .entry(descriptor.identifier.clone())
281                .or_default()
282                .push(i);
283        }
284        let provider = Self {
285            root: Rc::new(Root {
286                descriptors,
287                lookup,
288                options,
289                scope: scope.clone(),
290                resolving: RefCell::new(Vec::new()),
291                validated: RefCell::new(HashSet::new()),
292            }),
293            scope,
294        };
295        if options.validate_on_build {
296            let mut done = HashSet::new();
297            let errors: Vec<_> = provider
298                .root
299                .descriptors
300                .iter()
301                .enumerate()
302                .filter_map(|(i, d)| {
303                    provider
304                        .validate(
305                            i,
306                            &d.identifier.service_key,
307                            &mut Vec::new(),
308                            &mut done,
309                            false,
310                            false,
311                        )
312                        .err()
313                })
314                .collect();
315            if !errors.is_empty() {
316                return Err(ServiceError::Validation(errors));
317            }
318        }
319        Ok(provider)
320    }
321    pub(crate) fn ensure_active(&self) -> ServiceResult<()> {
322        if self.scope.disposed.get() || self.root.scope.disposed.get() {
323            Err(ServiceError::Disposed)
324        } else {
325            Ok(())
326        }
327    }
328    /// Whether both handles are the same provider or scope.
329    pub fn same_as(&self, other: &ServiceProvider) -> bool {
330        Rc::ptr_eq(&self.root, &other.root) && Rc::ptr_eq(&self.scope, &other.scope)
331    }
332    pub fn is_disposed(&self) -> bool {
333        self.ensure_active().is_err()
334    }
335    pub fn create_scope(&self) -> ServiceResult<ServiceScope> {
336        self.ensure_active()?;
337        Ok(ServiceScope {
338            provider: Self {
339                root: self.root.clone(),
340                scope: Rc::new(ScopeState::default()),
341            },
342        })
343    }
344    pub fn create_async_scope(&self) -> ServiceResult<AsyncServiceScope> {
345        Ok(AsyncServiceScope::new(Rc::new(self.create_scope()?)))
346    }
347    pub fn get_service<S: ?Sized + 'static>(&self) -> ServiceResult<Option<Rc<S>>> {
348        ServiceProviderServiceExtensions::get_service(self)
349    }
350    pub fn get_required_service<S: ?Sized + 'static>(&self) -> ServiceResult<Rc<S>> {
351        ServiceProviderServiceExtensions::get_required_service(self)
352    }
353    pub fn get_services<S: ?Sized + 'static>(&self) -> ServiceResult<Vec<Rc<S>>> {
354        ServiceProviderServiceExtensions::get_services(self)
355    }
356    pub fn get_keyed_service<S: ?Sized + 'static>(
357        &self,
358        key: Option<&ServiceKey>,
359    ) -> ServiceResult<Option<Rc<S>>> {
360        ServiceProviderServiceExtensions::get_keyed_service(self, key)
361    }
362    pub fn get_required_keyed_service<S: ?Sized + 'static>(
363        &self,
364        key: Option<&ServiceKey>,
365    ) -> ServiceResult<Rc<S>> {
366        ServiceProviderServiceExtensions::get_required_keyed_service(self, key)
367    }
368    pub fn get_keyed_services<S: ?Sized + 'static>(
369        &self,
370        key: Option<&ServiceKey>,
371    ) -> ServiceResult<Vec<Rc<S>>> {
372        ServiceProviderServiceExtensions::get_keyed_services(self, key)
373    }
374    fn is_builtin(service_type: ServiceType) -> bool {
375        [
376            ServiceType::of::<dyn IServiceProvider>(),
377            ServiceType::of::<dyn IKeyedServiceProvider>(),
378            ServiceType::of::<dyn IServiceScopeFactory>(),
379            ServiceType::of::<dyn IServiceProviderIsService>(),
380            ServiceType::of::<dyn IServiceProviderIsKeyedService>(),
381        ]
382        .contains(&service_type)
383    }
384    fn builtin(&self, id: &ServiceIdentifier) -> Option<ErasedService> {
385        if id.service_key.is_some() {
386            return None;
387        }
388        let t = id.service_type;
389        fn cached<T: ?Sized + 'static>(
390            slot: &RefCell<Option<Weak<T>>>,
391            create: impl FnOnce() -> Rc<T>,
392        ) -> ErasedService {
393            if let Some(value) = slot.borrow().as_ref().and_then(Weak::upgrade) {
394                return erase_service(value);
395            }
396            let value = create();
397            *slot.borrow_mut() = Some(Rc::downgrade(&value));
398            erase_service(value)
399        }
400        if t == ServiceType::of::<dyn IServiceProvider>() {
401            return Some(cached(&self.scope.provider, || Rc::new(self.clone())));
402        }
403        if t == ServiceType::of::<dyn IKeyedServiceProvider>() {
404            return Some(cached(&self.scope.keyed_provider, || Rc::new(self.clone())));
405        }
406        let root = Self {
407            root: self.root.clone(),
408            scope: self.root.scope.clone(),
409        };
410        if t == ServiceType::of::<dyn IServiceScopeFactory>() {
411            return Some(cached(&self.root.scope.scope_factory, || Rc::new(root)));
412        }
413        if t == ServiceType::of::<dyn IServiceProviderIsService>() {
414            return Some(cached(&self.root.scope.is_service, || Rc::new(root)));
415        }
416        if t == ServiceType::of::<dyn IServiceProviderIsKeyedService>() {
417            return Some(cached(&self.root.scope.is_keyed_service, || Rc::new(root)));
418        }
419        None
420    }
421    fn matching(&self, id: &ServiceIdentifier, all: bool) -> ServiceResult<Vec<CacheKey>> {
422        if id.service_key.as_ref().is_some_and(ServiceKey::is_any) {
423            if !all {
424                return Err(ServiceError::AnyKeySingleResolution);
425            }
426            return Ok(self
427                .root
428                .descriptors
429                .iter()
430                .enumerate()
431                .filter(|(_, d)| {
432                    d.service_type() == id.service_type
433                        && d.service_key().is_some_and(|k| !k.is_any())
434                })
435                .map(|(index, d)| CacheKey {
436                    index,
437                    key: d.identifier.service_key.clone(),
438                })
439                .collect());
440        }
441        if let Some(indices) = self.root.lookup.get(id) {
442            return Ok(if all {
443                indices
444                    .iter()
445                    .map(|&index| CacheKey {
446                        index,
447                        key: id.service_key.clone(),
448                    })
449                    .collect()
450            } else {
451                vec![CacheKey {
452                    index: *indices.last().unwrap(),
453                    key: id.service_key.clone(),
454                }]
455            });
456        }
457        // AnyKey is a fallback for a single lookup, never for enumeration.
458        if !all && id.service_key.is_some() {
459            let any = ServiceIdentifier {
460                service_type: id.service_type,
461                service_key: Some(ServiceKey::ANY),
462            };
463            if let Some(indices) = self.root.lookup.get(&any) {
464                return Ok(vec![CacheKey {
465                    index: *indices.last().unwrap(),
466                    key: id.service_key.clone(),
467                }]);
468            }
469        }
470        Ok(Vec::new())
471    }
472    /// Checks the dependencies below a registration: scoped services reached from a singleton or
473    /// the root, missing services and cycles. `done` holds the subtrees already found valid in
474    /// this pass, with the two flags they were checked under, so that a graph whose branches meet
475    /// again (diamonds) is checked in linear time rather than once per path.
476    fn validate(
477        &self,
478        index: usize,
479        key: &Option<ServiceKey>,
480        path: &mut Vec<CacheKey>,
481        done: &mut HashSet<(CacheKey, bool, bool)>,
482        singleton: bool,
483        root_resolution: bool,
484    ) -> ServiceResult<()> {
485        let d = &self.root.descriptors[index];
486        let id = ServiceIdentifier {
487            service_type: d.service_type(),
488            service_key: key.clone(),
489        };
490        if self.root.options.validate_scopes && d.lifetime == ServiceLifetime::Scoped {
491            if singleton {
492                return Err(ServiceError::ScopedInSingleton(id));
493            }
494            if root_resolution {
495                return Err(ServiceError::ScopedFromRoot(id));
496            }
497        }
498        let cache_key = CacheKey {
499            index,
500            key: key.clone(),
501        };
502        // A subtree found valid has no cycle, so it cannot lead back to the current path either.
503        if done.contains(&(cache_key.clone(), singleton, root_resolution)) {
504            return Ok(());
505        }
506        if path.contains(&cache_key) {
507            let mut ids: Vec<_> = path
508                .iter()
509                .map(|k| ServiceIdentifier {
510                    service_type: self.root.descriptors[k.index].service_type(),
511                    service_key: k.key.clone(),
512                })
513                .collect();
514            ids.push(id);
515            return Err(ServiceError::CircularDependency(ids));
516        }
517        let memo = (cache_key.clone(), singleton, root_resolution);
518        path.push(cache_key);
519        let singleton = singleton || d.lifetime == ServiceLifetime::Singleton;
520        for dependency in &d.dependencies {
521            let id = dependency.identifier_for(key);
522            if id.service_key.is_none() && Self::is_builtin(id.service_type) {
523                continue;
524            }
525            let matches = if dependency.kind == DependencyKind::Enumerable
526                && self.enumerable_override(&id).is_some()
527            {
528                vec![self.enumerable_override(&id).expect("enumerable override")]
529            } else if dependency.lookup_mode == ServiceKeyLookupMode::InheritKey
530                && key.as_ref().is_some_and(ServiceKey::is_any)
531                && dependency.kind != DependencyKind::Enumerable
532            {
533                self.root
534                    .lookup
535                    .get(&id)
536                    .and_then(|indices| indices.last())
537                    .map(|&index| {
538                        vec![CacheKey {
539                            index,
540                            key: key.clone(),
541                        }]
542                    })
543                    .unwrap_or_default()
544            } else {
545                self.matching(&id, dependency.kind == DependencyKind::Enumerable)?
546            };
547            if matches.is_empty() && dependency.kind == DependencyKind::Required {
548                return Err(ServiceError::MissingService(id));
549            }
550            for child in matches {
551                self.validate(
552                    child.index,
553                    &child.key,
554                    path,
555                    done,
556                    singleton,
557                    root_resolution,
558                )?;
559            }
560        }
561        path.pop();
562        done.insert(memo);
563        Ok(())
564    }
565    fn enumerable_override(&self, id: &ServiceIdentifier) -> Option<CacheKey> {
566        self.root
567            .descriptors
568            .iter()
569            .enumerate()
570            .rev()
571            .find_map(|(index, d)| {
572                (d.enumerable
573                    .as_ref()
574                    .is_some_and(|(element, _)| *element == id.service_type)
575                    && d.service_key() == id.service_key.as_ref())
576                .then(|| CacheKey {
577                    index,
578                    key: id.service_key.clone(),
579                })
580            })
581    }
582    fn resolve(&self, key: CacheKey) -> ServiceResult<ErasedService> {
583        self.ensure_active()?;
584        let d = &self.root.descriptors[key.index];
585        let provider = if d.lifetime == ServiceLifetime::Singleton {
586            Self {
587                root: self.root.clone(),
588                scope: self.root.scope.clone(),
589            }
590        } else {
591            self.clone()
592        };
593        let validation_key = (key.clone(), Rc::ptr_eq(&self.scope, &self.root.scope));
594        if !self.root.validated.borrow().contains(&validation_key) {
595            self.validate(
596                key.index,
597                &key.key,
598                &mut Vec::new(),
599                &mut HashSet::new(),
600                false,
601                validation_key.1,
602            )?;
603            // The set is a cache: keys of catch-all registrations come from callers and are not
604            // bounded, so it starts again past a size.
605            let mut validated = self.root.validated.borrow_mut();
606            if validated.len() >= MAX_VALIDATED {
607                validated.clear();
608            }
609            validated.insert(validation_key);
610        }
611        if d.lifetime != ServiceLifetime::Transient {
612            if let Some(value) = provider.scope.cache.borrow().get(&key) {
613                return Ok(value.clone());
614            }
615        }
616        let context = (key.clone(), Rc::as_ptr(&provider.scope) as usize);
617        if self.root.resolving.borrow().contains(&context) {
618            let mut ids: Vec<_> = self
619                .root
620                .resolving
621                .borrow()
622                .iter()
623                .map(|(k, _)| ServiceIdentifier {
624                    service_type: self.root.descriptors[k.index].service_type(),
625                    service_key: k.key.clone(),
626                })
627                .collect();
628            ids.push(ServiceIdentifier {
629                service_type: d.service_type(),
630                service_key: key.key.clone(),
631            });
632            return Err(ServiceError::CircularDependency(ids));
633        }
634        self.root.resolving.borrow_mut().push(context);
635        let _guard = ResolutionGuard(self.root.clone());
636        let value = (d.factory)(&provider, key.key.as_ref())?;
637        if d.dispose.is_some() || d.dispose_async.is_some() {
638            let captured = Captured {
639                value: value.clone(),
640                dispose: d.dispose.clone(),
641                dispose_async: d.dispose_async.clone(),
642            };
643            if provider.is_disposed() {
644                // A user factory can dispose its scope while constructing. Never publish that value.
645                if let Some(dispose) = &captured.dispose {
646                    dispose(&captured.value);
647                } else if let Some(dispose) = &captured.dispose_async {
648                    // Tasks are already running on the local executor; dropping the handle does not cancel them.
649                    drop(dispose(&captured.value));
650                }
651                return Err(ServiceError::Disposed);
652            }
653            provider.scope.disposables.borrow_mut().push(captured);
654        }
655        provider.ensure_active()?;
656        if d.lifetime != ServiceLifetime::Transient {
657            provider.scope.cache.borrow_mut().insert(key, value.clone());
658        }
659        Ok(value)
660    }
661    /// Disposes captured services in reverse construction order. Async-only services require
662    /// `dispose_async`; that error leaves the scope active so the caller can retry asynchronously.
663    pub fn dispose(&self) -> ServiceResult<()> {
664        if self.scope.disposed.get() {
665            return Ok(());
666        }
667        if self
668            .scope
669            .disposables
670            .borrow()
671            .iter()
672            .any(|d| d.dispose.is_none() && d.dispose_async.is_some())
673        {
674            return Err(ServiceError::AsyncDisposalRequired);
675        }
676        self.scope.disposed.set(true);
677        let disposables = std::mem::take(&mut *self.scope.disposables.borrow_mut());
678        let cache = std::mem::take(&mut *self.scope.cache.borrow_mut());
679        for captured in disposables.into_iter().rev() {
680            if let Some(dispose) = captured.dispose {
681                dispose(&captured.value);
682            }
683        }
684        drop(cache);
685        Ok(())
686    }
687    pub fn dispose_async(&self) -> Task<()> {
688        if self.scope.disposed.replace(true) {
689            return Task::from_result(());
690        }
691        let disposables = std::mem::take(&mut *self.scope.disposables.borrow_mut());
692        let cache = std::mem::take(&mut *self.scope.cache.borrow_mut());
693        Task::spawn_local(async move {
694            for captured in disposables.into_iter().rev() {
695                if let Some(dispose) = captured.dispose_async {
696                    dispose(&captured.value).await?;
697                } else if let Some(dispose) = captured.dispose {
698                    dispose(&captured.value);
699                }
700            }
701            drop(cache);
702            Ok(())
703        })
704    }
705}
706impl IServiceProvider for ServiceProvider {
707    fn ensure_active(&self) -> ServiceResult<()> {
708        self.ensure_active()
709    }
710    fn required_service_provider(&self) -> Option<&dyn ISupportRequiredService> {
711        Some(self)
712    }
713    fn disposable(&self) -> Option<&dyn IDisposable> {
714        Some(self)
715    }
716    fn get_service_erased(&self, id: &ServiceIdentifier) -> ServiceResult<Option<ErasedService>> {
717        self.ensure_active()?;
718        if let Some(value) = self.builtin(id) {
719            return Ok(Some(value));
720        }
721        self.matching(id, false)?
722            .into_iter()
723            .next()
724            .map(|key| self.resolve(key))
725            .transpose()
726    }
727    fn get_services_erased(&self, id: &ServiceIdentifier) -> ServiceResult<Vec<ErasedService>> {
728        self.ensure_active()?;
729        if let Some(key) = self.enumerable_override(id) {
730            let enumerate = self.root.descriptors[key.index]
731                .enumerable
732                .as_ref()
733                .expect("enumerable")
734                .1;
735            return Ok(enumerate(&self.resolve(key)?));
736        }
737        self.matching(id, true)?
738            .into_iter()
739            .map(|key| self.resolve(key))
740            .collect()
741    }
742}
743impl IKeyedServiceProvider for ServiceProvider {}
744impl IServiceProviderIsService for ServiceProvider {
745    fn is_service(&self, service_type: ServiceType) -> bool {
746        self.is_keyed_service(service_type, None)
747    }
748}
749impl IServiceProviderIsKeyedService for ServiceProvider {
750    fn is_keyed_service(&self, service_type: ServiceType, key: Option<&ServiceKey>) -> bool {
751        (key.is_none() && Self::is_builtin(service_type))
752            || self.root.lookup.contains_key(&ServiceIdentifier {
753                service_type,
754                service_key: key.cloned(),
755            })
756            || (key.is_some()
757                && self.root.lookup.contains_key(&ServiceIdentifier {
758                    service_type,
759                    service_key: Some(ServiceKey::ANY),
760                }))
761    }
762}
763impl IServiceScopeFactory for ServiceProvider {
764    fn create_scope(&self) -> ServiceResult<ServiceScope> {
765        self.create_scope()
766    }
767}
768// Deviation (DEVIATIONS.md, Dependency injection): upstream's Dispose throws when a service has
769// only asynchronous disposal; the trait method has no result, so it panics with that error.
770impl IDisposable for ServiceProvider {
771    fn dispose(&self) {
772        self.dispose().unwrap_or_else(|e| panic!("{e}"));
773    }
774}
775impl IAsyncDisposable for ServiceProvider {
776    fn dispose_async(&self) -> Task<()> {
777        self.dispose_async()
778    }
779}