Skip to main content

rust_dix/
provider.rs

1use crate::cache::{AnyService, KeyedInstanceCache, LazyCache};
2use crate::entry::{
3    AsyncServiceFactory, IServiceResolver, ServiceEntry, ServiceFactory, ServiceLifetime,
4    ServiceStore,
5};
6use crate::error::RdiError;
7use crate::key::ServiceKey;
8use crate::scope::ScopeFactory;
9use crate::validation::ValidationResolver;
10use std::any::{Any, TypeId};
11use std::cell::{Cell, RefCell};
12use std::collections::HashMap;
13use std::future::Future;
14use std::panic::AssertUnwindSafe;
15use std::pin::Pin;
16use std::sync::atomic::{AtomicBool, Ordering};
17use std::sync::{Arc, Mutex, OnceLock, RwLock, Weak};
18use std::task::{Context, Poll};
19
20thread_local! {
21    /// Runtime resolution stack for Scoped/Transient cycle detection.
22    static RESOLVING: RefCell<Vec<(ResolveToken, &'static str)>> = const { RefCell::new(Vec::new()) };
23    /// Non-zero while a build-time singleton factory is executing.
24    ///
25    /// Resolving services through an injected provider inside a constructor
26    /// would bypass build-time cycle/captive validation *and* make the outcome
27    /// depend on store iteration order, so it is rejected deterministically.
28    static BUILD_FACTORY_DEPTH: Cell<usize> = const { Cell::new(0) };
29    /// Resolution chain of the async factory currently being polled on this
30    /// thread, captured by public `get_async` futures at construction time so
31    /// nested resolution inherits the chain even when the future is polled on
32    /// another task/thread later.
33    static CURRENT_ASYNC_CHAIN: RefCell<Option<Arc<ResolveChainNode>>> =
34        const { RefCell::new(None) };
35}
36
37/// Identity of a resolution frame for cycle detection.
38///
39/// `AnyKey` fallback registrations are one entry shared by many requested keys,
40/// so their frame identity includes the requested key hash: nested resolution of
41/// the *same* key is a cycle, while resolving a different key is legitimate.
42#[derive(Clone, Copy, PartialEq, Eq)]
43pub(crate) enum ResolveToken {
44    CacheKey(usize),
45    AnyKey(usize, u64),
46}
47
48pub(crate) struct ResolveGuard;
49
50impl Drop for ResolveGuard {
51    fn drop(&mut self) {
52        RESOLVING.with(|stack| {
53            stack.borrow_mut().pop();
54        });
55    }
56}
57
58/// RAII marker for “a build-time singleton factory is currently executing”.
59pub(crate) struct BuildFactoryGuard;
60
61impl BuildFactoryGuard {
62    pub(crate) fn enter() -> Self {
63        BUILD_FACTORY_DEPTH.with(|depth| depth.set(depth.get() + 1));
64        Self
65    }
66}
67
68impl Drop for BuildFactoryGuard {
69    fn drop(&mut self) {
70        BUILD_FACTORY_DEPTH.with(|depth| depth.set(depth.get().saturating_sub(1)));
71    }
72}
73
74/// Reject provider-mediated resolution while a constructor is running.
75///
76/// Construction should declare dependencies as fields; dynamic resolution must
77/// happen later in methods, where no build-time validation constraint applies.
78fn reject_build_time_provider_resolution(type_name: &'static str) {
79    if BUILD_FACTORY_DEPTH.with(|depth| depth.get()) > 0 {
80        std::panic::panic_any(RdiError::FactoryPanic {
81            service: type_name.to_string(),
82            payload: format!(
83                "resolving services through the injected provider from a build-time constructor \
84                 is not supported: store the provider/ScopeFactory and resolve lazily in methods \
85                 (attempted to resolve {type_name})"
86            ),
87        });
88    }
89}
90
91pub(crate) fn enter_resolve(
92    cache_key: usize,
93    type_name: &'static str,
94) -> Result<ResolveGuard, RdiError> {
95    enter_resolve_token(ResolveToken::CacheKey(cache_key), type_name)
96}
97
98pub(crate) fn enter_resolve_token(
99    token: ResolveToken,
100    type_name: &'static str,
101) -> Result<ResolveGuard, RdiError> {
102    RESOLVING.with(|stack| {
103        let mut s = stack.borrow_mut();
104        if s.iter().any(|(k, _)| *k == token) {
105            let mut chain: Vec<&str> = s.iter().map(|(_, n)| *n).collect();
106            chain.push(type_name);
107            return Err(RdiError::CircularDependency(chain.join(" -> ")));
108        }
109        s.push((token, type_name));
110        Ok(ResolveGuard)
111    })
112}
113
114/// Immutable async resolution chain node (persistent list).
115///
116/// Carried inside async resolution futures (and [`AsyncFactoryContext`]) so
117/// cycle detection works across `await` points and even across tasks when the
118/// context is propagated — no thread-local state is required.
119pub(crate) struct ResolveChainNode {
120    pub(crate) cache_key: usize,
121    pub(crate) type_name: &'static str,
122    pub(crate) parent: Option<Arc<ResolveChainNode>>,
123}
124
125/// Snapshot of the chain of the async factory currently being polled.
126fn current_async_chain() -> Option<Arc<ResolveChainNode>> {
127    CURRENT_ASYNC_CHAIN.with(|chain| chain.borrow().clone())
128}
129
130struct ChainScope {
131    previous: Option<Arc<ResolveChainNode>>,
132}
133
134impl ChainScope {
135    fn enter(chain: Option<Arc<ResolveChainNode>>) -> Self {
136        let previous = CURRENT_ASYNC_CHAIN.with(|current| current.replace(chain));
137        Self { previous }
138    }
139}
140
141impl Drop for ChainScope {
142    fn drop(&mut self) {
143        let previous = self.previous.take();
144        CURRENT_ASYNC_CHAIN.with(|current| {
145            *current.borrow_mut() = previous;
146        });
147    }
148}
149
150/// Future wrapper that exposes `chain` as the current async chain while polling.
151struct ChainScopeFuture<F> {
152    inner: Pin<Box<F>>,
153    chain: Option<Arc<ResolveChainNode>>,
154}
155
156impl<F: Future> Future for ChainScopeFuture<F> {
157    type Output = F::Output;
158
159    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
160        // `Self` is `Unpin` (it only holds a `Pin<Box<F>>` and an `Arc`).
161        let this = self.get_mut();
162        let _scope = ChainScope::enter(this.chain.clone());
163        this.inner.as_mut().poll(cx)
164    }
165}
166
167fn chain_scope<F: Future>(chain: Option<Arc<ResolveChainNode>>, future: F) -> ChainScopeFuture<F> {
168    ChainScopeFuture {
169        inner: Box::pin(future),
170        chain,
171    }
172}
173
174fn chain_contains(chain: &Option<Arc<ResolveChainNode>>, cache_key: usize) -> Option<String> {
175    let mut path = Vec::new();
176    let mut found = false;
177    let mut current = chain.clone();
178    while let Some(node) = current {
179        path.push(node.type_name);
180        if node.cache_key == cache_key {
181            found = true;
182        }
183        current = node.parent.clone();
184    }
185    if found {
186        path.reverse();
187        Some(path.join(" -> "))
188    } else {
189        None
190    }
191}
192
193/// Create the next chain node for `entry` and fail if it is already in the chain.
194fn extend_chain(
195    chain: &Option<Arc<ResolveChainNode>>,
196    entry: &ServiceEntry,
197) -> Result<Option<Arc<ResolveChainNode>>, RdiError> {
198    if let Some(cycle) = chain_contains(chain, entry.cache_key) {
199        return Err(RdiError::CircularDependency(format!(
200            "{cycle} -> {}",
201            entry.type_name
202        )));
203    }
204    Ok(Some(Arc::new(ResolveChainNode {
205        cache_key: entry.cache_key,
206        type_name: entry.type_name,
207        parent: chain.clone(),
208    })))
209}
210
211/// A resolved keyed lookup: either an exact keyed registration or the `AnyKey`
212/// fallback registration (resolved with the requested key).
213enum KeyedLookup<'a> {
214    Exact(&'a ServiceEntry),
215    Fallback(&'a ServiceEntry),
216}
217
218/// Drop cached instances, collecting (instead of propagating) `Drop` panics.
219///
220/// Disposal must finish releasing the remaining instances even when a user
221/// `Drop` impl panics; the first panic is re-raised by the caller after all
222/// cleanup work completed (and swallowed during unwinding).
223fn drop_instances_safely(instances: Vec<AnyService>) -> Option<Box<dyn Any + Send>> {
224    let mut first_panic: Option<Box<dyn Any + Send>> = None;
225    for instance in instances {
226        if let Err(payload) = std::panic::catch_unwind(AssertUnwindSafe(|| drop(instance))) {
227            if first_panic.is_none() {
228                first_panic = Some(payload);
229            }
230        }
231    }
232    first_panic
233}
234
235/// Options for building a [`ServiceProvider`].
236///
237/// `#[non_exhaustive]`: construct via [`ServiceProviderOptions::new`] and the
238/// `with_*` builder methods (`Default` equals `new()`), so future options stay
239/// additive.
240#[derive(Debug, Clone, Copy, PartialEq, Eq)]
241#[non_exhaustive]
242pub struct ServiceProviderOptions {
243    /// Reject resolving `Scoped` services from the root provider.
244    ///
245    /// Mirrors MEDI `ValidateScopes` as enabled by the .NET (ASP.NET Core) host
246    /// in the Development environment: resolving a scoped service outside a
247    /// scope is a misconfiguration, because the root caches it for the container
248    /// lifetime (the "scoped becomes singleton" anti-pattern).
249    ///
250    /// - `true` (default): `get`/`get_async` (and constructor dependencies)
251    ///   return [`RdiError::ScopedFromRootProvider`] when they would resolve a
252    ///   `Scoped` service from the root provider. Infallible lookups
253    ///   (`get_optional`, `get_all`, `get_all_keyed`, `try_*`) treat such a
254    ///   service as not resolvable at the root. `get_owned` is exempt for the
255    ///   owned instance itself (it is caller-owned and never enters a scope
256    ///   cache); its transitive dependencies are still validated.
257    /// - `false`: restores rust-dix ≤ 0.7 root-scope behavior (the root provider
258    ///   caches scoped instances and acts as the root scope).
259    pub validate_scopes: bool,
260}
261
262impl ServiceProviderOptions {
263    /// Strict defaults: `validate_scopes = true`.
264    pub fn new() -> Self {
265        Self {
266            validate_scopes: true,
267        }
268    }
269
270    /// Set scope validation explicitly.
271    pub fn with_validate_scopes(mut self, validate_scopes: bool) -> Self {
272        self.validate_scopes = validate_scopes;
273        self
274    }
275
276    /// Convenience preset matching MEDI's bare `BuildServiceProvider()`:
277    /// scope validation disabled (root-scope resolution allowed).
278    pub fn lenient() -> Self {
279        Self {
280            validate_scopes: false,
281        }
282    }
283}
284
285impl Default for ServiceProviderOptions {
286    fn default() -> Self {
287        Self::new()
288    }
289}
290
291/// State shared between the root provider and every scope created from it.
292///
293/// Disposing the root marks the whole tree disposed; scopes created earlier can
294/// no longer resolve services.
295pub(crate) struct SharedProviderState {
296    disposed: AtomicBool,
297}
298
299impl SharedProviderState {
300    fn new() -> Self {
301        Self {
302            disposed: AtomicBool::new(false),
303        }
304    }
305}
306
307pub struct ServiceProvider {
308    /// Service registry. Shared across root + scope-derived providers via `Arc`.
309    store: Arc<ServiceStore>,
310    /// String → TypeId lookup for IServiceResolver string-based resolution.
311    /// Shared across root + scope-derived providers via `Arc`.
312    type_map: Arc<HashMap<&'static str, TypeId>>,
313    /// Singleton 实例缓存(per-key `OnceLock`,多线程下工厂只执行一次)。
314    /// build 阶段 validation + eager init 预热,运行时 lazy fallback。
315    /// Shared across root + scope-derived providers via `Arc`(全局单例语义)。
316    /// 记录首次创建顺序,供 [`Self::dispose`] 逆序释放。
317    singleton_cache: Arc<LazyCache>,
318    /// 当前 provider 的 Scoped 实例缓存。
319    ///
320    /// Root provider 即 root scope:`validate_scopes = true`(默认)时 root 不允许
321    /// 解析 Scoped 服务;`validate_scopes = false` 时实例在此缓存中复用,语义等同
322    /// 于在根 scope 内单例化。子 Scope(通过 [`ServiceProvider::scope`] 创建)拥有
323    /// 独立的 scoped 缓存,不回退到根。per-key `OnceLock` 保证多线程下工厂只执行
324    /// 一次,无 TOCTOU 竞态。**每个 provider 独立**。
325    root_scoped_cache: LazyCache,
326    /// `AnyKey` fallback singleton instances, one per requested key.
327    /// Shared across root + scope-derived providers via `Arc`(全局语义).
328    any_singleton_cache: Arc<KeyedInstanceCache>,
329    /// `AnyKey` fallback scoped instances for this provider (one per requested key).
330    any_scoped_cache: KeyedInstanceCache,
331    /// String-keyed registry for cross-DLL (cdylib) service access.
332    /// Rust's `TypeId` differs across compilation units, so named
333    /// lookup is the only reliable mechanism for plugin services.
334    /// Shared across root + scope-derived providers via `Arc`.
335    pub(crate) named: Arc<RwLock<HashMap<String, Arc<dyn Any + Send + Sync>>>>,
336    /// Self-referential weak pointer, initialized by [`Self::new`] or
337    /// [`Self::new_scope`]. Enables [`IServiceResolver::provider_arc`] to hand
338    /// out `Arc<ServiceProvider>` to services using `#[inject(provider)]`.
339    /// **每个 provider 独立**:scope-derived provider 的 self_weak 指向自身。
340    self_weak: OnceLock<Weak<ServiceProvider>>,
341    /// Built-in `dyn ScopeFactory` singleton, created lazily on first resolution.
342    /// It holds a `Weak` provider reference, so caching it cannot create an
343    /// `Arc` cycle (MEDI parity: the scope factory is itself a singleton).
344    scope_factory_cache: OnceLock<Arc<dyn ScopeFactory>>,
345    /// Cleanup closures for scoped services registered via [`Self::register_disposable`].
346    /// Populated during factory execution, drained (reverse registration order) by
347    /// `Self::dispose`.
348    /// **每个 provider 独立**:scope-derived provider 的 disposables 绑定到当前 scope。
349    disposables: Mutex<Vec<Box<dyn FnOnce() + Send>>>,
350    /// Root/scope shared state (`disposed` flag). Root dispose marks the tree dead.
351    shared: Arc<SharedProviderState>,
352    /// This provider disposed flag (root dispose or scope dispose/Drop).
353    disposed: AtomicBool,
354    /// Root provider (not a scope): only the root applies scope validation.
355    is_root: bool,
356    /// Whether Scoped services may be resolved from this root provider.
357    validate_scopes: bool,
358}
359
360impl ServiceProvider {
361    pub(crate) fn new_with(
362        store: ServiceStore,
363        options: ServiceProviderOptions,
364    ) -> Result<Arc<Self>, RdiError> {
365        // Reject async registrations on the sync build path.
366        if let Some(entry) = store
367            .values()
368            .flat_map(|entries| entries.iter())
369            .find(|e| e.is_async())
370        {
371            return Err(RdiError::AsyncServiceRequiresBuildAsync {
372                service: entry.type_name.to_string(),
373            });
374        }
375
376        // Build type_name → TypeId lookup table for string-based resolution
377        let mut type_map = HashMap::new();
378        for (&tid, entries) in &store {
379            if let Some(e) = entries.first() {
380                type_map.entry(e.type_name).or_insert(tid);
381            }
382        }
383
384        let singleton_cache = Arc::new(LazyCache::new());
385
386        // Phase 1: create the provider in an `Arc` **before** dependency
387        // validation. Singleton factories may legitimately depend on the
388        // container itself (`#[inject(provider)]`, `#[inject] Arc<dyn
389        // ScopeFactory>`), so `provider_arc()` and the built-in ScopeFactory
390        // must be resolvable while validation executes those factories.
391        let sp_arc = Arc::new(Self {
392            store: Arc::new(store),
393            type_map: Arc::new(type_map),
394            singleton_cache,
395            root_scoped_cache: LazyCache::new(),
396            any_singleton_cache: Arc::new(KeyedInstanceCache::new()),
397            any_scoped_cache: KeyedInstanceCache::new(),
398            named: Arc::new(RwLock::new(HashMap::new())),
399            self_weak: OnceLock::new(),
400            scope_factory_cache: OnceLock::new(),
401            disposables: Mutex::new(Vec::new()),
402            shared: Arc::new(SharedProviderState::new()),
403            disposed: AtomicBool::new(false),
404            is_root: true,
405            validate_scopes: options.validate_scopes,
406        });
407        sp_arc.self_weak.get_or_init(|| Arc::downgrade(&sp_arc));
408
409        // Phase 2: Validate dependencies (cycle detection, captive dependency)
410        // and cache the singleton instances built along the way.
411        Self::validate_dependencies(&sp_arc)?;
412
413        // Phase 3: eagerly execute any sync singleton whose factory was not
414        // already run by validation.
415        Self::init_sync_singletons(&sp_arc);
416        Ok(sp_arc)
417    }
418
419    /// Collect sync singleton entries in **registration order** and eagerly
420    /// execute their factories.
421    ///
422    /// Reuses instances cached by validation (build time) so factories aren't
423    /// double-fired. Called by both `new` and `new_async` (async singletons skipped).
424    ///
425    /// Ordering is deterministic (sorted by `cache_key` = registration index)
426    /// instead of relying on `HashMap` iteration order, so side effects and
427    /// validation errors are reproducible across runs.
428    fn init_sync_singletons(sp_arc: &Arc<Self>) {
429        let mut singleton_entries: Vec<(usize, ServiceFactory)> = sp_arc
430            .store
431            .values()
432            .flat_map(|entries| entries.iter())
433            .filter(|e| {
434                e.lifetime == ServiceLifetime::Singleton && !e.is_async() && !e.is_any_key()
435            })
436            .map(|e| (e.cache_key, e.factory.clone()))
437            .collect();
438        singleton_entries.sort_by_key(|(cache_key, _)| *cache_key);
439
440        for (ck, factory) in &singleton_entries {
441            let resolver = sp_arc.as_ref() as &dyn IServiceResolver;
442            let factory = factory.clone();
443            sp_arc
444                .singleton_cache
445                .get_or_init_with(*ck, move || factory(resolver));
446        }
447    }
448
449    /// Build a `ServiceProvider` with async factory support.
450    ///
451    /// Async singleton factories are executed during build and their results
452    /// are cached. Async transient/scoped factories are stored for later
453    /// resolution via `get_async` / `get_keyed_async`.
454    ///
455    /// Returns `Arc<ServiceProvider>` so that `provider_arc()` works for
456    /// async resolution (async factories receive `Arc<ServiceProvider>`).
457    pub(crate) async fn new_async_with(
458        store: ServiceStore,
459        options: ServiceProviderOptions,
460    ) -> Result<Arc<Self>, RdiError> {
461        // Build type_name → TypeId lookup
462        let mut type_map = HashMap::new();
463        for (&tid, entries) in &store {
464            if let Some(e) = entries.first() {
465                type_map.entry(e.type_name).or_insert(tid);
466            }
467        }
468
469        let singleton_cache = Arc::new(LazyCache::new());
470
471        // Phase 1: create the provider in an Arc **before** validation so that
472        // singleton factories depending on `ServiceProvider` / `ScopeFactory`
473        // can be constructed eagerly (same contract as the sync `build()`).
474        let sp_arc = Arc::new(Self {
475            store: Arc::new(store),
476            type_map: Arc::new(type_map),
477            singleton_cache,
478            root_scoped_cache: LazyCache::new(),
479            any_singleton_cache: Arc::new(KeyedInstanceCache::new()),
480            any_scoped_cache: KeyedInstanceCache::new(),
481            named: Arc::new(RwLock::new(HashMap::new())),
482            self_weak: OnceLock::new(),
483            scope_factory_cache: OnceLock::new(),
484            disposables: Mutex::new(Vec::new()),
485            shared: Arc::new(SharedProviderState::new()),
486            disposed: AtomicBool::new(false),
487            is_root: true,
488            validate_scopes: options.validate_scopes,
489        });
490        sp_arc.self_weak.get_or_init(|| Arc::downgrade(&sp_arc));
491
492        // Phase 2: validate sync singleton dependencies and cache instances.
493        Self::validate_dependencies(&sp_arc)?;
494
495        // Phase 3: eagerly execute sync singletons not already built.
496        Self::init_sync_singletons(&sp_arc);
497
498        // Phase 4: initialize async singletons dependency-first.
499        //
500        // Registration order must not matter: an async singleton factory may
501        // `await` another async singleton registered later. `ensure_async_singleton`
502        // recursively initializes dependencies (single-flight, per-poll cycle
503        // detection), so the resulting creation order is a deterministic
504        // dependency-first topological order rather than registration order.
505        let mut async_singletons: Vec<&ServiceEntry> = sp_arc
506            .store
507            .values()
508            .flat_map(|entries| entries.iter())
509            .filter(|e| e.lifetime == ServiceLifetime::Singleton && e.is_async() && !e.is_any_key())
510            .collect();
511        async_singletons.sort_by_key(|e| e.cache_key);
512
513        for entry in async_singletons {
514            sp_arc.ensure_async_singleton(entry, None).await?;
515        }
516
517        Ok(sp_arc)
518    }
519
520    /// Ensure that an async singleton entry is initialized exactly once.
521    ///
522    /// Dependency-driven and registration-order independent: if a factory
523    /// `await`s an async singleton that has not been initialized yet, the nested
524    /// `get_async` call re-enters this method and recursively initializes the
525    /// dependency first (single-flight via the shared async cache).
526    ///
527    /// The resolution chain is checked/extended before execution, so recursive
528    /// self/cross cycles (including across tasks when the context is propagated)
529    /// return [`RdiError::CircularDependency`] instead of dead-locking.
530    /// Also used as a lazy safety net by `get_async` after build.
531    async fn ensure_async_singleton(
532        &self,
533        entry: &ServiceEntry,
534        chain: Option<Arc<ResolveChainNode>>,
535    ) -> Result<(), RdiError> {
536        self.check_alive()?;
537        if self.singleton_cache.get(entry.cache_key).is_some() {
538            return Ok(());
539        }
540        let Some(async_factory) = entry.async_factory.clone() else {
541            // Synchronous singleton: already eagerly initialized by build.
542            return Ok(());
543        };
544
545        let node = extend_chain(&chain, entry)?;
546        let type_name = entry.type_name;
547        let provider_arc = self.provider_arc().ok_or_else(|| RdiError::FactoryPanic {
548            service: type_name.to_string(),
549            payload: "provider_arc() returned None for async singleton initialization".into(),
550        })?;
551
552        self.singleton_cache
553            .get_or_init_with_async(entry.cache_key, || {
554                chain_scope(
555                    node.clone(),
556                    Self::run_async_factory_safely(type_name, async_factory, provider_arc),
557                )
558            })
559            .await?;
560        Ok(())
561    }
562
563    /// 创建一个 scope 范围的 ServiceProvider,共享 store/type_map/singleton_cache/named,
564    /// 独立 scoped 缓存、disposables 与 self_weak。
565    ///
566    /// 供 [`Scope::new`](crate::scope::Scope::new) 调用。这使 `#[inject(provider)]`
567    /// 字段拿到的是 **scope 范围的 provider** 而非根 provider——经该字段解析 Scoped 服务
568    /// 会命中 scope 自己的 scoped 缓存,符合 MEDI `IServiceScope` 语义。
569    pub(crate) fn new_scope(parent: &Arc<Self>) -> Arc<Self> {
570        let sp = Self {
571            store: Arc::clone(&parent.store),
572            type_map: Arc::clone(&parent.type_map),
573            singleton_cache: Arc::clone(&parent.singleton_cache),
574            root_scoped_cache: LazyCache::new(),
575            any_singleton_cache: Arc::clone(&parent.any_singleton_cache),
576            any_scoped_cache: KeyedInstanceCache::new(),
577            named: Arc::clone(&parent.named),
578            self_weak: OnceLock::new(),
579            scope_factory_cache: OnceLock::new(),
580            disposables: Mutex::new(Vec::new()),
581            shared: Arc::clone(&parent.shared),
582            disposed: AtomicBool::new(false),
583            is_root: false,
584            validate_scopes: parent.validate_scopes,
585        };
586        let arc = Arc::new(sp);
587        let this: &Self = &arc;
588        this.self_weak.get_or_init(|| Arc::downgrade(&arc));
589        arc
590    }
591
592    /// Register a cleanup closure for scoped services/owned resources.
593    ///
594    /// Closures run (in **reverse registration order**) when
595    /// [`Self::dispose`] is called — either explicitly or by [`Scope`](crate::Scope)'s
596    /// `Drop` for scope providers.
597    pub fn register_disposable(&self, cleanup: Box<dyn FnOnce() + Send>) {
598        self.disposables.lock().unwrap().push(cleanup);
599    }
600
601    /// Dispose this provider: release cached instances in reverse creation order,
602    /// run registered cleanups in reverse registration order, and mark the
603    /// provider unusable.
604    ///
605    /// - Root provider: also marks the whole provider tree disposed (existing
606    ///   scopes can no longer resolve), drains the singleton cache, and clears
607    ///   named registrations. Singleton instances are released in reverse
608    ///   creation order (LIFO), so dependents drop before their dependencies.
609    /// - Scope provider: releases the scoped instances created in that scope and
610    ///   runs its cleanups. Singletons are untouched.
611    ///
612    /// Idempotent. Instances are released by dropping the container's strong
613    /// references; a service's `Drop` runs when no other owner keeps it alive.
614    /// Services obtained via `get_owned` are caller-owned and are not tracked.
615    ///
616    /// The caller must not resolve services concurrently with disposal (same
617    /// contract as MEDI's `ServiceProvider.Dispose`). A panicking cleanup closure
618    /// does not stop the remaining cleanups; the first panic is re-raised after
619    /// disposal (and swallowed when the thread is already unwinding, e.g. during
620    /// `Scope`'s `Drop`).
621    pub fn dispose(&self) {
622        if self.disposed.swap(true, Ordering::SeqCst) {
623            return;
624        }
625        if self.is_root {
626            self.shared.disposed.store(true, Ordering::SeqCst);
627        }
628
629        // Release instances (reverse LIFO order). User `Drop` panics are
630        // collected so the remaining instances/cleanups still run.
631        let mut first_panic = drop_instances_safely(self.root_scoped_cache.drain_reverse());
632        let any_scoped_panic = drop_instances_safely(self.any_scoped_cache.drain_reverse());
633        if first_panic.is_none() {
634            first_panic = any_scoped_panic;
635        }
636
637        if self.is_root {
638            // Release singletons (reverse creation order) and clear plugins.
639            let singleton_panic = drop_instances_safely(self.singleton_cache.drain_reverse());
640            if first_panic.is_none() {
641                first_panic = singleton_panic;
642            }
643            let any_singleton_panic =
644                drop_instances_safely(self.any_singleton_cache.drain_reverse());
645            if first_panic.is_none() {
646                first_panic = any_singleton_panic;
647            }
648            self.named.write().unwrap().clear();
649        }
650
651        let cleanup_panic = self.run_cleanups();
652        if first_panic.is_none() {
653            first_panic = cleanup_panic;
654        }
655
656        if let Some(payload) = first_panic {
657            if !std::thread::panicking() {
658                std::panic::resume_unwind(payload);
659            }
660        }
661    }
662
663    /// Drain and execute cleanup closures in reverse registration order.
664    ///
665    /// The first cleanup panic is returned to the caller instead of being
666    /// raised here, so disposal can finish all remaining work first. During
667    /// unwinding (drop path) the panic is swallowed to avoid a double-panic
668    /// abort.
669    fn run_cleanups(&self) -> Option<Box<dyn Any + Send>> {
670        let mut cleanups: Vec<Box<dyn FnOnce() + Send>> = {
671            let mut guard = self.disposables.lock().unwrap();
672            std::mem::take(&mut *guard)
673        };
674        cleanups.reverse();
675
676        let mut first_panic: Option<Box<dyn Any + Send>> = None;
677        for cleanup in cleanups {
678            if let Err(payload) = std::panic::catch_unwind(AssertUnwindSafe(cleanup)) {
679                if first_panic.is_none() {
680                    first_panic = Some(payload);
681                }
682            }
683        }
684        first_panic
685    }
686
687    /// Validate all dependencies: detect cycles and captive dependencies.
688    /// Instances built during validation are stored in the provider's singleton
689    /// cache so they can be reused by the subsequent eager-init phase.
690    ///
691    /// Validation runs **after** the provider `Arc` exists: factories may inject
692    /// the provider itself (`#[inject(provider)]`) or the built-in
693    /// [`ScopeFactory`], which requires `provider_arc()` and `self_weak` to be
694    /// live. The validation resolver still executes factories itself, so
695    /// cycle/captive-dependency accounting is preserved.
696    ///
697    /// Validation order is deterministic (sorted by `cache_key` = registration
698    /// index), so the first reported error is stable across runs.
699    fn validate_dependencies(sp: &Arc<Self>) -> Result<(), RdiError> {
700        // Build TypeId → type_name map for error messages
701        let type_names: HashMap<TypeId, &'static str> = sp
702            .store
703            .iter()
704            .map(|(&tid, entries)| {
705                let name = entries.first().map(|e| e.type_name).unwrap_or("<unknown>");
706                (tid, name)
707            })
708            .collect();
709
710        let mut singletons: Vec<&ServiceEntry> = sp
711            .store
712            .values()
713            .flat_map(|entries| entries.iter())
714            .filter(|e| {
715                e.lifetime == ServiceLifetime::Singleton
716                    && !e.is_async()
717                    // AnyKey fallbacks need a requested key; they are not eagerly
718                    // validated/initialized.
719                    && !e.is_any_key()
720            })
721            .collect();
722        singletons.sort_by_key(|e| e.cache_key);
723
724        // Validate each sync singleton by attempting to resolve it.
725        // Scoped/Transient are not eagerly executed at build (avoids side effects);
726        // their cycles are detected at runtime via a thread-local resolution stack.
727        //
728        // Each registration is validated individually: duplicate registrations
729        // (MEDI override semantics) are all constructed eagerly, and each factory
730        // must pass the cycle/captive checks under its own cache key.
731        for entry in singletons {
732            let resolver = ValidationResolver::new(
733                &sp.store,
734                &type_names,
735                &sp.singleton_cache,
736                Arc::clone(sp),
737                entry.type_name,
738            );
739            let type_name = entry.type_name;
740
741            let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
742                resolver.validate_entry(entry)
743            }));
744
745            if let Some(err) = resolver.take_error() {
746                return Err(err);
747            }
748
749            match result {
750                Ok(_) => {}
751                Err(panic_info) => {
752                    return Err(RdiError::from_panic_payload(type_name, panic_info));
753                }
754            }
755        }
756
757        Ok(())
758    }
759
760    // ── Registration lookup (single-service resolution: last registration wins) ──
761
762    /// Last unkeyed registration for `tid` (MEDI: later registrations override
763    /// earlier ones when resolving a single service).
764    fn unkeyed_entry(&self, tid: TypeId) -> Option<&ServiceEntry> {
765        self.store.get(&tid)?.iter().rev().find(|e| e.key.is_none())
766    }
767
768    /// Find the registration for `tid` + `key`.
769    ///
770    /// Exact keyed registrations win (last registration for that key); otherwise
771    /// the `AnyKey` fallback registration is used. Requesting the `AnyKey`
772    /// sentinel itself is not a valid single-service resolution (returns `None`).
773    fn keyed_entry(&self, tid: TypeId, key: &ServiceKey) -> Option<KeyedLookup<'_>> {
774        let entries = self.store.get(&tid)?;
775        if key.is_any() {
776            return None;
777        }
778        if let Some(entry) = entries.iter().rev().find(|e| e.key.as_ref() == Some(key)) {
779            return Some(KeyedLookup::Exact(entry));
780        }
781        entries
782            .iter()
783            .rev()
784            .find(|e| e.is_any_key())
785            .map(KeyedLookup::Fallback)
786    }
787
788    fn entry_by_str(&self, type_name: &str, key: Option<&ServiceKey>) -> Option<&ServiceEntry> {
789        let tid = *self.type_map.get(type_name)?;
790        match key {
791            Some(k) => match self.keyed_entry(tid, k)? {
792                KeyedLookup::Exact(entry) | KeyedLookup::Fallback(entry) => Some(entry),
793            },
794            None => self.unkeyed_entry(tid),
795        }
796    }
797
798    /// Resolve a keyed lookup, applying the `AnyKey` fallback with the requested key.
799    fn resolve_keyed_lookup(
800        &self,
801        lookup: KeyedLookup<'_>,
802        requested: &ServiceKey,
803    ) -> Result<AnyService, RdiError> {
804        match lookup {
805            KeyedLookup::Exact(entry) => self.resolve_entry_catching(entry),
806            KeyedLookup::Fallback(entry) => self.resolve_any_keyed_entry(entry, requested, false),
807        }
808    }
809
810    /// Execute an `AnyKey` fallback entry for `requested`.
811    ///
812    /// Singleton/scoped instances are cached **per requested key**; transient
813    /// instances are created per resolution. When `owned` is set the cache is
814    /// bypassed (caller-owned instance) and only non-singleton lifetimes are
815    /// allowed.
816    fn resolve_any_keyed_entry(
817        &self,
818        entry: &ServiceEntry,
819        requested: &ServiceKey,
820        owned: bool,
821    ) -> Result<AnyService, RdiError> {
822        // Owned instances bypass the scope cache, so the owned instance itself is
823        // exempt from root scope validation (same as the exact-key owned path);
824        // its transitive dependencies are still validated.
825        self.check_alive()?;
826        if !owned {
827            self.check_scope_validation(entry)?;
828        }
829        let Some(factory) = entry.keyed_factory.clone() else {
830            return Err(RdiError::keyed_not_found(
831                entry.type_name,
832                requested.to_string(),
833                None,
834            ));
835        };
836        if entry.lifetime == ServiceLifetime::Singleton && owned {
837            return Err(RdiError::OwnedNotSupported(entry.type_name));
838        }
839
840        let resolver = self as &dyn IServiceResolver;
841        let requested_owned = requested.clone();
842        let call = move || (factory)(resolver, &requested_owned);
843        let lifetime = entry.lifetime;
844        // `AnyKey` entries are shared by many requested keys: the frame identity
845        // includes the requested key, so nested resolution of a *different* key
846        // is not a false cycle.
847        let _guard = enter_resolve_token(
848            ResolveToken::AnyKey(entry.cache_key, requested.stable_hash()),
849            entry.type_name,
850        )?;
851        let result = std::panic::catch_unwind(AssertUnwindSafe(move || match (lifetime, owned) {
852            (ServiceLifetime::Singleton, false) => {
853                self.any_singleton_cache
854                    .get_or_init_with(entry.cache_key, requested, call)
855            }
856            (ServiceLifetime::Scoped, false) => {
857                self.any_scoped_cache
858                    .get_or_init_with(entry.cache_key, requested, call)
859            }
860            _ => call(),
861        }));
862        match result {
863            Ok(value) => Ok(value),
864            Err(payload) => Err(RdiError::from_panic_payload(entry.type_name, payload)),
865        }
866    }
867
868    /// Cache a per-key `AnyKey` singleton instance created during build-time
869    /// validation, so runtime resolution reuses the same instance (single
870    /// initialization, identity preserved).
871    pub(crate) fn prefill_any_keyed_singleton(
872        &self,
873        cache_key: usize,
874        requested: &ServiceKey,
875        instance: AnyService,
876    ) {
877        self.any_singleton_cache
878            .prefill(cache_key, requested, instance);
879    }
880
881    /// Already-built `AnyKey` singleton for `(registration, requested key)`, if any.
882    ///
883    /// Used by build-time validation so a second consumer of the same requested
884    /// key reuses the cached instance instead of constructing another one.
885    pub(crate) fn cached_any_singleton(
886        &self,
887        cache_key: usize,
888        requested: &ServiceKey,
889    ) -> Option<AnyService> {
890        self.any_singleton_cache.get(cache_key, requested)
891    }
892
893    // ── Liveness / scope validation ──
894
895    /// Whether this provider (or its root) has been disposed.
896    fn check_alive(&self) -> Result<(), RdiError> {
897        if self.shared.disposed.load(Ordering::SeqCst) {
898            return Err(RdiError::ProviderDisposed);
899        }
900        if self.disposed.load(Ordering::SeqCst) {
901            return Err(RdiError::ScopeDisposed);
902        }
903        Ok(())
904    }
905
906    /// MEDI `ValidateScopes` equivalent: a scoped service must not be resolved
907    /// from the root provider (it would be cached for the container lifetime).
908    fn check_scope_validation(&self, entry: &ServiceEntry) -> Result<(), RdiError> {
909        if entry.lifetime == ServiceLifetime::Scoped && self.is_root && self.validate_scopes {
910            return Err(RdiError::ScopedFromRootProvider {
911                service: entry.type_name.to_string(),
912            });
913        }
914        Ok(())
915    }
916
917    fn check_resolvable(&self, entry: &ServiceEntry) -> Result<(), RdiError> {
918        self.check_alive()?;
919        self.check_scope_validation(entry)
920    }
921
922    // ── Resolution cores ──
923
924    /// Resolve a single entry, converting liveness/scope violations, cycles and
925    /// factory panics into typed [`RdiError`].
926    fn resolve_entry_catching(&self, entry: &ServiceEntry) -> Result<AnyService, RdiError> {
927        self.check_resolvable(entry)?;
928        match std::panic::catch_unwind(AssertUnwindSafe(|| self.get_any_by_entry(entry))) {
929            Ok(Some(v)) => Ok(v),
930            Ok(None) => Err(RdiError::not_found(entry.type_name, None)),
931            Err(payload) => Err(RdiError::from_panic_payload(entry.type_name, payload)),
932        }
933    }
934
935    /// Resolve a single entry, panicking with a typed [`RdiError`] on liveness or
936    /// scope violations (used by [`IServiceResolver`], whose contract is
937    /// infallible `Option`; required-resolution APIs catch these panics and
938    /// return `Err`).
939    fn resolve_entry_panicking(&self, entry: &ServiceEntry) -> AnyService {
940        if let Err(err) = self.check_resolvable(entry) {
941            std::panic::panic_any(err);
942        }
943        match self.get_any_by_entry(entry) {
944            Some(v) => v,
945            None => std::panic::panic_any(RdiError::not_found(entry.type_name, None)),
946        }
947    }
948
949    /// Type-erased resolution by type name, for the resolver trait.
950    ///
951    /// Single-service semantics: **last registration wins** (MEDI), both for
952    /// unkeyed and for same-key keyed registrations.
953    fn resolve_unkeyed_optional<T: ?Sized + Send + Sync + 'static>(
954        &self,
955    ) -> Result<Option<Arc<T>>, RdiError> {
956        self.check_alive()?;
957        if let Some(entry) = self.unkeyed_entry(TypeId::of::<T>()) {
958            return Ok(Self::extract(self.resolve_entry_catching(entry)?));
959        }
960        // Built-in container services (ScopeFactory / ServiceProvider) are
961        // always available from a built provider; explicit registrations win
962        // because the store lookup above runs first.
963        Ok(self
964            .builtin_service_by_type_id(TypeId::of::<T>())
965            .and_then(Self::extract))
966    }
967
968    /// Resolve a service by type. Works uniformly for concrete types and trait objects.
969    /// Returns `Err(RdiError::ServiceNotFound)` if not registered.
970    ///
971    /// [`ScopeFactory`] is always available from a built provider (no manual
972    /// registration required). An explicit `singleton::<dyn ScopeFactory>(...)`
973    /// registration still takes precedence. Later registrations override earlier
974    /// ones (MEDI semantics).
975    pub fn get<T: ?Sized + Send + Sync + 'static>(&self) -> Result<Arc<T>, RdiError> {
976        let type_name = std::any::type_name::<T>();
977        match self.resolve_unkeyed_optional::<T>() {
978            Ok(Some(v)) => Ok(v),
979            Ok(None) => Err(RdiError::not_found(type_name, None)),
980            Err(e) => Err(e),
981        }
982    }
983
984    /// Resolve a service by type, returning `None` if it is not registered **or
985    /// not resolvable at this point** (e.g. a scoped service under root scope
986    /// validation, or a disposed provider). Use [`Self::get`] when you need the
987    /// concrete error.
988    pub fn get_optional<T: ?Sized + Send + Sync + 'static>(&self) -> Option<Arc<T>> {
989        self.resolve_unkeyed_optional::<T>().ok().flatten()
990    }
991
992    /// Resolve an async service by type.
993    ///
994    /// The returned future captures the **current async resolution chain** at
995    /// construction time (synchronously), so nested resolution inherits the
996    /// chain even when the future is moved to another task. For fully detached
997    /// task closures, propagate [`Self::async_context`] (see its docs).
998    pub fn get_async<T: ?Sized + Send + Sync + 'static>(
999        &self,
1000    ) -> impl Future<Output = Result<Arc<T>, RdiError>> + Send + '_ {
1001        let chain = current_async_chain();
1002        async move { self.resolve_async_unkeyed::<T>(chain).await }
1003    }
1004
1005    /// Resolve an async keyed service by type and key.
1006    ///
1007    /// Accepts string keys and typed [`ServiceKey`] values. `AnyKey` fallback
1008    /// registrations are synchronous by design and are resolved through the
1009    /// synchronous keyed path.
1010    pub fn get_keyed_async<T: ?Sized + Send + Sync + 'static>(
1011        &self,
1012        key: impl Into<ServiceKey> + Send,
1013    ) -> impl Future<Output = Result<Arc<T>, RdiError>> + Send + '_ {
1014        let chain = current_async_chain();
1015        let key = key.into();
1016        async move { self.resolve_async_keyed::<T>(chain, key).await }
1017    }
1018
1019    async fn resolve_async_unkeyed<T: ?Sized + Send + Sync + 'static>(
1020        &self,
1021        chain: Option<Arc<ResolveChainNode>>,
1022    ) -> Result<Arc<T>, RdiError> {
1023        let type_name = std::any::type_name::<T>();
1024        let entry = self
1025            .unkeyed_entry(TypeId::of::<T>())
1026            .ok_or_else(|| RdiError::not_found(type_name, None))?;
1027        self.resolve_async_entry::<T>(entry, chain).await
1028    }
1029
1030    async fn resolve_async_keyed<T: ?Sized + Send + Sync + 'static>(
1031        &self,
1032        chain: Option<Arc<ResolveChainNode>>,
1033        key: ServiceKey,
1034    ) -> Result<Arc<T>, RdiError> {
1035        let type_name = std::any::type_name::<T>();
1036        let Some(lookup) = self.keyed_entry(TypeId::of::<T>(), &key) else {
1037            return Err(RdiError::keyed_not_found(type_name, key.to_string(), None));
1038        };
1039        match lookup {
1040            KeyedLookup::Exact(entry) => self.resolve_async_entry::<T>(entry, chain).await,
1041            KeyedLookup::Fallback(entry) => {
1042                // AnyKey fallbacks are sync-only in this version.
1043                if entry.is_async() {
1044                    return Err(RdiError::AsyncServiceRequiresGetAsync {
1045                        service: entry.type_name.to_string(),
1046                    });
1047                }
1048                let any = self.resolve_any_keyed_entry(entry, &key, false)?;
1049                Self::extract(any)
1050                    .ok_or_else(|| RdiError::keyed_not_found(type_name, key.to_string(), None))
1051            }
1052        }
1053    }
1054
1055    /// Resolve an entry through the async path, falling back to the synchronous
1056    /// path for sync-registered services.
1057    async fn resolve_async_entry<T: ?Sized + Send + Sync + 'static>(
1058        &self,
1059        entry: &ServiceEntry,
1060        chain: Option<Arc<ResolveChainNode>>,
1061    ) -> Result<Arc<T>, RdiError> {
1062        if entry.lifetime == ServiceLifetime::Singleton {
1063            // Async singletons are initialized dependency-first; this is also a
1064            // lazy safety net after build.
1065            self.ensure_async_singleton(entry, chain).await?;
1066            return self.resolve_entry_as::<T>(entry);
1067        }
1068        self.check_resolvable(entry)?;
1069        match self.resolve_async_non_singleton(entry, chain).await? {
1070            Some(any) => {
1071                Self::extract(any).ok_or_else(|| RdiError::not_found(entry.type_name, None))
1072            }
1073            None => self.resolve_entry_as::<T>(entry),
1074        }
1075    }
1076
1077    /// Resolve an entry and extract `Arc<T>` (typed errors, no panics for
1078    /// framework failures).
1079    fn resolve_entry_as<T: ?Sized + Send + Sync + 'static>(
1080        &self,
1081        entry: &ServiceEntry,
1082    ) -> Result<Arc<T>, RdiError> {
1083        let any = self.resolve_entry_catching(entry)?;
1084        Self::extract(any).ok_or_else(|| RdiError::not_found(entry.type_name, None))
1085    }
1086
1087    /// Resolve an async Scoped/Transient factory, returning `Some(AnyService)`.
1088    /// Returns `None` when the entry has no async factory (sync-registered).
1089    ///
1090    /// The dependency chain is threaded through the returned future, so
1091    /// self/cross cycles are detected deterministically (including across
1092    /// `await` points and tasks when the context is propagated) instead of
1093    /// dead-locking on the per-key async cache lock.
1094    async fn resolve_async_non_singleton(
1095        &self,
1096        entry: &ServiceEntry,
1097        chain: Option<Arc<ResolveChainNode>>,
1098    ) -> Result<Option<AnyService>, RdiError> {
1099        let Some(async_factory) = entry.async_factory.clone() else {
1100            return Ok(None);
1101        };
1102        let type_name = entry.type_name;
1103        let node = extend_chain(&chain, entry)?;
1104        let provider_arc = self.provider_arc().ok_or_else(|| RdiError::FactoryPanic {
1105            service: type_name.to_string(),
1106            payload: "provider_arc() returned None for async resolution".into(),
1107        })?;
1108        match entry.lifetime {
1109            ServiceLifetime::Scoped => self
1110                .root_scoped_cache
1111                .get_or_init_with_async(entry.cache_key, || {
1112                    chain_scope(
1113                        node.clone(),
1114                        Self::run_async_factory_safely(type_name, async_factory, provider_arc),
1115                    )
1116                })
1117                .await
1118                .map(Some),
1119            ServiceLifetime::Transient => chain_scope(
1120                node,
1121                Self::run_async_factory_safely(type_name, async_factory, provider_arc),
1122            )
1123            .await
1124            .map(Some),
1125            _ => Ok(None),
1126        }
1127    }
1128
1129    /// Execute an async factory and catch panics, converting to `RdiError::FactoryPanic`.
1130    ///
1131    /// async 工厂 panic 无法自然传播到 `.await` 点之外(会终止 task),
1132    /// 需用 `catch_unwind` 捕获并转为类型安全的 `RdiError`。
1133    async fn run_async_factory_safely(
1134        type_name: &'static str,
1135        async_factory: AsyncServiceFactory,
1136        provider_arc: Arc<ServiceProvider>,
1137    ) -> Result<AnyService, RdiError> {
1138        let fut = async_factory(provider_arc);
1139        match futures::FutureExt::catch_unwind(AssertUnwindSafe(fut)).await {
1140            Ok(arc_any) => Ok(arc_any),
1141            Err(payload) => Err(RdiError::from_panic_payload(type_name, payload)),
1142        }
1143    }
1144
1145    /// Resolve a keyed service by type and key.
1146    /// Resolve a keyed service by type and key.
1147    ///
1148    /// Accepts string keys (`"name"`) as well as typed [`ServiceKey`] values.
1149    /// Exact-key registrations take precedence; otherwise an `AnyKey` fallback
1150    /// registration is used for the requested key.
1151    /// Returns `Err(RdiError::KeyedServiceNotFound)` if not found.
1152    pub fn get_keyed<T: ?Sized + Send + Sync + 'static>(
1153        &self,
1154        key: impl Into<ServiceKey>,
1155    ) -> Result<Arc<T>, RdiError> {
1156        let key = key.into();
1157        let type_name = std::any::type_name::<T>();
1158        match self.resolve_keyed_optional::<T>(&key)? {
1159            Some(value) => Ok(value),
1160            None => Err(RdiError::keyed_not_found(type_name, key.to_string(), None)),
1161        }
1162    }
1163
1164    fn resolve_keyed_optional<T: ?Sized + Send + Sync + 'static>(
1165        &self,
1166        key: &ServiceKey,
1167    ) -> Result<Option<Arc<T>>, RdiError> {
1168        self.check_alive()?;
1169        let Some(lookup) = self.keyed_entry(TypeId::of::<T>(), key) else {
1170            return Ok(None);
1171        };
1172        let any = self.resolve_keyed_lookup(lookup, key)?;
1173        Ok(Self::extract(any))
1174    }
1175
1176    /// Resolve owned `T` (bypass cache, fresh each call).
1177    /// Returns `Err` if not registered or the service is Singleton.
1178    ///
1179    /// Owned resolution hands the instance to the caller: it is not tracked by
1180    /// the container and stays valid after `dispose`. Scoped ownership bypasses
1181    /// the scope cache, so a root scope-validation error is not raised for the
1182    /// owned instance itself; its *dependencies* still obey scope validation.
1183    pub fn get_owned<T: Send + Sync + 'static>(&self) -> Result<T, RdiError> {
1184        let type_name = std::any::type_name::<T>();
1185        let tid = TypeId::of::<T>();
1186        let entry = self
1187            .unkeyed_entry(tid)
1188            .ok_or_else(|| RdiError::not_found(type_name, None))?;
1189        if entry.lifetime == ServiceLifetime::Singleton {
1190            return Err(RdiError::OwnedNotSupported(type_name));
1191        }
1192        self.check_alive()?;
1193        let arc_any = match std::panic::catch_unwind(AssertUnwindSafe(|| {
1194            self.create_owned_any_by_entry(entry)
1195        })) {
1196            Ok(Some(v)) => v,
1197            Ok(None) => return Err(RdiError::not_found(type_name, None)),
1198            Err(payload) => return Err(RdiError::from_panic_payload(type_name, payload)),
1199        };
1200        let arc_t = Self::extract(arc_any).ok_or_else(|| RdiError::not_found(type_name, None))?;
1201        Arc::try_unwrap(arc_t).map_err(|_| RdiError::OwnedNotSupported(type_name))
1202    }
1203
1204    /// Resolve owned `T`, returning `None` if not registered, a Singleton, or the
1205    /// provider/scope is disposed.
1206    pub fn try_get_owned<T: Send + Sync + 'static>(&self) -> Option<T> {
1207        self.get_owned().ok()
1208    }
1209
1210    /// Resolve owned keyed `T`.
1211    /// Returns `Err` if not found or Singleton.
1212    pub fn get_keyed_owned<T: Send + Sync + 'static>(
1213        &self,
1214        key: impl Into<ServiceKey>,
1215    ) -> Result<T, RdiError> {
1216        let key = key.into();
1217        let type_name = std::any::type_name::<T>();
1218        let tid = TypeId::of::<T>();
1219        let Some(lookup) = self.keyed_entry(tid, &key) else {
1220            return Err(RdiError::keyed_not_found(type_name, key.to_string(), None));
1221        };
1222        let (entry, any) = match lookup {
1223            KeyedLookup::Exact(entry) => {
1224                if entry.lifetime == ServiceLifetime::Singleton {
1225                    return Err(RdiError::OwnedNotSupported(type_name));
1226                }
1227                self.check_alive()?;
1228                let arc_any = match std::panic::catch_unwind(AssertUnwindSafe(|| {
1229                    self.create_owned_any_by_entry(entry)
1230                })) {
1231                    Ok(Some(v)) => v,
1232                    Ok(None) => {
1233                        return Err(RdiError::keyed_not_found(type_name, key.to_string(), None))
1234                    }
1235                    Err(payload) => return Err(RdiError::from_panic_payload(type_name, payload)),
1236                };
1237                (entry, arc_any)
1238            }
1239            KeyedLookup::Fallback(entry) => {
1240                if entry.lifetime == ServiceLifetime::Singleton {
1241                    return Err(RdiError::OwnedNotSupported(type_name));
1242                }
1243                self.check_alive()?;
1244                (entry, self.resolve_any_keyed_entry(entry, &key, true)?)
1245            }
1246        };
1247        let _ = entry;
1248        let arc_t = Self::extract(any)
1249            .ok_or_else(|| RdiError::keyed_not_found(type_name, key.to_string(), None))?;
1250        Arc::try_unwrap(arc_t).map_err(|_| RdiError::OwnedNotSupported(type_name))
1251    }
1252
1253    /// Resolve owned keyed `T`, returning `None` if not found, a Singleton, or
1254    /// the provider/scope is disposed.
1255    pub fn try_get_keyed_owned<T: Send + Sync + 'static>(
1256        &self,
1257        key: impl Into<ServiceKey>,
1258    ) -> Option<T> {
1259        self.get_keyed_owned(key).ok()
1260    }
1261
1262    /// Return all **unkeyed** registered instances of the given type, in
1263    /// registration order (MEDI `GetServices<T>()` / `IEnumerable<T>` parity).
1264    ///
1265    /// Keyed implementations are excluded — use [`Self::get_all_keyed`] for
1266    /// those. Under root scope validation, scoped services are not resolvable at
1267    /// the root and are omitted; other resolution failures panic with a typed
1268    /// [`RdiError`] payload (mirroring MEDI, where enumeration throws).
1269    pub fn get_all<T: ?Sized + Send + Sync + 'static>(&self) -> Vec<Arc<T>> {
1270        self.collect_all::<T>(false)
1271    }
1272
1273    /// Return all **keyed** registered instances of the given type, in
1274    /// registration order (MEDI `GetKeyedServices<T>(KeyedService.AnyKey)` parity).
1275    ///
1276    /// `AnyKey` fallback registrations are excluded: MEDI's `AnyKey` enumeration
1277    /// returns only registrations with an explicit key.
1278    pub fn get_all_keyed<T: ?Sized + Send + Sync + 'static>(&self) -> Vec<Arc<T>> {
1279        self.collect_all::<T>(true)
1280    }
1281
1282    fn collect_all<T: ?Sized + Send + Sync + 'static>(&self, keyed_only: bool) -> Vec<Arc<T>> {
1283        if self.check_alive().is_err() {
1284            return Vec::new();
1285        }
1286        let Some(entries) = self.store.get(&TypeId::of::<T>()) else {
1287            return Vec::new();
1288        };
1289        let mut out = Vec::new();
1290        for entry in entries.iter().filter(|e| {
1291            if keyed_only {
1292                e.key.is_some() && !e.is_any_key()
1293            } else {
1294                e.key.is_none()
1295            }
1296        }) {
1297            match self.resolve_entry_catching(entry) {
1298                Ok(any) => {
1299                    if let Some(v) = Self::extract(any) {
1300                        out.push(v);
1301                    }
1302                }
1303                // Root scope validation: scoped services are not resolvable at
1304                // the root, so they do not appear in enumerations.
1305                Err(RdiError::ScopedFromRootProvider { .. }) => {}
1306                Err(err) => std::panic::panic_any(err),
1307            }
1308        }
1309        out
1310    }
1311
1312    /// Create a new service scope.
1313    ///
1314    /// Analogous to `IServiceProvider.CreateScope()` in MEDI.
1315    /// Scoped-lifetime services are cached within the returned scope.
1316    pub fn scope(self: &Arc<Self>) -> crate::scope::Scope {
1317        crate::scope::Scope::new(self.clone())
1318    }
1319
1320    /// Whether `T` has a resolvable registration (or is a built-in container
1321    /// service). MEDI `IServiceProviderIsService` parity.
1322    ///
1323    /// Reports registration state only: it is still `true` after [`Self::dispose`].
1324    pub fn is_registered<T: ?Sized + Send + Sync + 'static>(&self) -> bool {
1325        let tid = TypeId::of::<T>();
1326        self.store
1327            .get(&tid)
1328            .is_some_and(|entries| entries.iter().any(|e| e.key.is_none()))
1329            || tid == TypeId::of::<dyn ScopeFactory>()
1330            || tid == TypeId::of::<ServiceProvider>()
1331    }
1332
1333    /// Whether `T` has a registration under `key` (exact key, or an `AnyKey`
1334    /// fallback registration). MEDI `IServiceProviderIsService.IsKeyedService` parity.
1335    pub fn is_registered_keyed<T: ?Sized + Send + Sync + 'static>(
1336        &self,
1337        key: impl Into<ServiceKey>,
1338    ) -> bool {
1339        let key = key.into();
1340        if key.is_any() {
1341            // The AnyKey sentinel is a registration-side fallback marker, not a
1342            // resolvable key.
1343            return false;
1344        }
1345        self.store.get(&TypeId::of::<T>()).is_some_and(|entries| {
1346            entries.iter().any(|e| e.key.as_ref() == Some(&key))
1347                || entries.iter().any(|e| e.is_any_key())
1348        })
1349    }
1350
1351    /// Built-in `dyn ScopeFactory` singleton for this provider (weak-backed).
1352    fn builtin_scope_factory(&self) -> Option<Arc<dyn ScopeFactory>> {
1353        let weak = self.self_weak.get()?.clone();
1354        Some(Arc::clone(
1355            self.scope_factory_cache
1356                .get_or_init(|| crate::scope::builtin_scope_factory(weak)),
1357        ))
1358    }
1359
1360    fn builtin_scope_factory_any(&self) -> Option<AnyService> {
1361        Some(crate::scope::builtin_scope_factory_any(
1362            self.builtin_scope_factory()?,
1363        ))
1364    }
1365
1366    /// Cached built-in `dyn ScopeFactory` service for the validation resolver.
1367    ///
1368    /// Exposed `pub(crate)` so eager singletons receive the same factory
1369    /// instance as `provider.get::<dyn ScopeFactory>()` (singleton identity).
1370    pub(crate) fn builtin_scope_factory_any_cached(&self) -> Option<AnyService> {
1371        self.builtin_scope_factory_any()
1372    }
1373
1374    /// Resolve container-provided (built-in) services that need no registration.
1375    ///
1376    /// - `dyn ScopeFactory`: weak-backed singleton factory that creates scopes.
1377    /// - `ServiceProvider`: the container itself (MEDI `IServiceProvider` parity),
1378    ///   enabling `#[inject] Arc<ServiceProvider>` in addition to the dedicated
1379    ///   `#[inject(provider)]` field attribute.
1380    fn builtin_service_by_type_id(&self, tid: TypeId) -> Option<AnyService> {
1381        if tid == TypeId::of::<dyn ScopeFactory>() {
1382            return self.builtin_scope_factory_any();
1383        }
1384        if tid == TypeId::of::<ServiceProvider>() {
1385            let provider = self.self_weak.get()?.upgrade()?;
1386            return Some(Arc::new(provider) as AnyService);
1387        }
1388        None
1389    }
1390
1391    /// String-keyed variant of [`Self::builtin_service_by_type_id`] for the
1392    /// `IServiceResolver::get_any` path used by the inject macros.
1393    fn builtin_service_by_name(&self, key: &str) -> Option<AnyService> {
1394        if key == std::any::type_name::<dyn ScopeFactory>() {
1395            return self.builtin_scope_factory_any();
1396        }
1397        if key == std::any::type_name::<ServiceProvider>() {
1398            let provider = self.self_weak.get()?.upgrade()?;
1399            return Some(Arc::new(provider) as AnyService);
1400        }
1401        None
1402    }
1403
1404    pub(crate) fn get_any_by_entry(&self, entry: &ServiceEntry) -> Option<AnyService> {
1405        reject_build_time_provider_resolution(entry.type_name);
1406        if entry.is_async() {
1407            // Sync resolution of an async service is only valid after its
1408            // build-time initialization cached the singleton.
1409            let ready = entry.lifetime == ServiceLifetime::Singleton
1410                && self.singleton_cache.get(entry.cache_key).is_some();
1411            if !ready {
1412                std::panic::panic_any(RdiError::AsyncServiceRequiresGetAsync {
1413                    service: entry.type_name.to_string(),
1414                });
1415            }
1416        }
1417        let _guard = match enter_resolve(entry.cache_key, entry.type_name) {
1418            Ok(g) => g,
1419            Err(e) => std::panic::panic_any(e),
1420        };
1421        match entry.lifetime {
1422            ServiceLifetime::Singleton => {
1423                // LazyCache::get_or_init_with:build 阶段已预热的走 fast path,
1424                // 未预热的(交叉引用边缘情况)懒执行工厂并缓存。
1425                // per-key OnceLock 保证多线程下工厂只执行一次,无 TOCTOU 竞态。
1426                // 工厂 panic 时 OnceLock 不缓存(std 行为),下次解析重试。
1427                let factory = entry.factory.clone();
1428                let resolver = self as &dyn IServiceResolver;
1429                Some(
1430                    self.singleton_cache
1431                        .get_or_init_with(entry.cache_key, move || factory(resolver)),
1432                )
1433            }
1434            ServiceLifetime::Scoped => {
1435                let factory = entry.factory.clone();
1436                let resolver = self as &dyn IServiceResolver;
1437                Some(
1438                    self.root_scoped_cache
1439                        .get_or_init_with(entry.cache_key, move || factory(resolver)),
1440                )
1441            }
1442            ServiceLifetime::Transient => Some((entry.factory)(self)),
1443        }
1444    }
1445
1446    /// Bypass cache and invoke the factory fresh (owned resolution only).
1447    ///
1448    /// - Singleton → `None` (shared singleton cannot be owned).
1449    /// - Scoped/Transient → invoke factory directly without touching
1450    ///   `singleton_cache` / scoped cache. The factory uses `self`
1451    ///   (ServiceProvider, i.e. current scope) as resolver, so Scoped
1452    ///   dependencies resolved transitively bind to this provider's scoped cache.
1453    pub(crate) fn create_owned_any_by_entry(&self, entry: &ServiceEntry) -> Option<AnyService> {
1454        reject_build_time_provider_resolution(entry.type_name);
1455        match entry.lifetime {
1456            ServiceLifetime::Singleton => None,
1457            ServiceLifetime::Scoped | ServiceLifetime::Transient => {
1458                let _guard = match enter_resolve(entry.cache_key, entry.type_name) {
1459                    Ok(g) => g,
1460                    Err(e) => std::panic::panic_any(e),
1461                };
1462                Some((entry.factory)(self as &dyn IServiceResolver))
1463            }
1464        }
1465    }
1466
1467    /// Extract `Arc<T>` from `Arc<Arc<T>>` stored inside `Arc<dyn Any>`.
1468    /// The factory double-wraps: inner `Arc<T>`, outer `Arc<dyn Any>`.
1469    pub(crate) fn extract<T: ?Sized + Send + Sync + 'static>(
1470        arc: Arc<dyn Any + Send + Sync>,
1471    ) -> Option<Arc<T>> {
1472        let double: Arc<Arc<T>> = arc.downcast::<Arc<T>>().ok()?;
1473        Some(Arc::clone(&*double))
1474    }
1475
1476    /// Cross-DLL safe named service resolution (generic).
1477    pub fn get_named<T: Send + Sync + 'static>(&self, name: &str) -> Option<Arc<T>> {
1478        self.named
1479            .read()
1480            .unwrap()
1481            .get(name)?
1482            .clone()
1483            .downcast::<T>()
1484            .ok()
1485    }
1486
1487    /// Non-generic named resolution; returns `Arc<dyn Any>` for trait-object dispatch.
1488    pub fn get_named_any(&self, name: &str) -> Option<Arc<dyn Any + Send + Sync>> {
1489        self.named.read().unwrap().get(name).cloned()
1490    }
1491
1492    /// Register a named service for cross-DLL plugin access.
1493    ///
1494    /// No-op after the provider/scope has been disposed (the named registry is
1495    /// cleared by [`Self::dispose`]).
1496    pub fn register_named<T: Send + Sync + 'static>(&self, name: &str, service: Arc<T>) {
1497        if self.check_alive().is_err() {
1498            return;
1499        }
1500        self.named
1501            .write()
1502            .unwrap()
1503            .insert(name.to_string(), service);
1504    }
1505
1506    /// Remove a named service (for plugin unload).
1507    pub fn remove_named(&self, name: &str) {
1508        if self.check_alive().is_err() {
1509            return;
1510        }
1511        self.named.write().unwrap().remove(name);
1512    }
1513
1514    /// Register a named service (for [`IProvider`](crate::bridge::IProvider) trait).
1515    ///
1516    /// No-op after the provider/scope has been disposed.
1517    pub fn rdi_register_named(&self, name: &str, service: Arc<dyn Any + Send + Sync>) {
1518        if self.check_alive().is_err() {
1519            return;
1520        }
1521        self.named
1522            .write()
1523            .unwrap()
1524            .insert(name.to_string(), service);
1525    }
1526
1527    /// Remove a named service (for [`IProvider`](crate::bridge::IProvider) trait).
1528    pub fn rdi_remove_named(&self, name: &str) {
1529        if self.check_alive().is_err() {
1530            return;
1531        }
1532        self.named.write().unwrap().remove(name);
1533    }
1534
1535    /// Built-in container services resolvable through the trait path, honoring
1536    /// liveness (a disposed provider no longer serves them; the violation is
1537    /// raised as a typed panic so required-resolution APIs convert it to `Err`).
1538    fn builtin_service_checked(&self, key: &str) -> Option<AnyService> {
1539        if let Err(err) = self.check_alive() {
1540            std::panic::panic_any(err);
1541        }
1542        self.builtin_service_by_name(key)
1543    }
1544
1545    fn builtin_service_by_type_id_checked(&self, tid: TypeId) -> Option<AnyService> {
1546        if let Err(err) = self.check_alive() {
1547            std::panic::panic_any(err);
1548        }
1549        self.builtin_service_by_type_id(tid)
1550    }
1551}
1552
1553/// Handle for propagating the async resolution chain into detached tasks.
1554///
1555/// Obtained from [`ServiceProvider::async_context`] **inside an async factory**
1556/// (the call captures the current chain synchronously). Clone it into a spawned
1557/// task and resolve through it; self/cross dependency cycles across tasks are
1558/// then reported as [`RdiError::CircularDependency`] instead of dead-locking.
1559///
1560/// ```rust,ignore
1561/// .async_singleton(|sp| Box::pin(async move {
1562///     let ctx = sp.async_context().expect("built provider");
1563///     let worker = tokio::spawn({
1564///         let ctx = ctx.clone();
1565///         async move { ctx.get_async::<Dep>().await }
1566///     });
1567///     let dep = worker.await.unwrap().unwrap();
1568///     Arc::new(Service { dep })
1569/// }))
1570/// ```
1571#[derive(Clone)]
1572pub struct AsyncFactoryContext {
1573    provider: Arc<ServiceProvider>,
1574    chain: Option<Arc<ResolveChainNode>>,
1575}
1576
1577impl AsyncFactoryContext {
1578    /// The container this context resolves from.
1579    pub fn provider(&self) -> &Arc<ServiceProvider> {
1580        &self.provider
1581    }
1582
1583    /// Resolve an async service, extending the propagated resolution chain.
1584    pub fn get_async<T: ?Sized + Send + Sync + 'static>(
1585        &self,
1586    ) -> impl Future<Output = Result<Arc<T>, RdiError>> + Send + 'static {
1587        let provider = Arc::clone(&self.provider);
1588        let chain = self.chain.clone();
1589        async move { provider.resolve_async_unkeyed::<T>(chain).await }
1590    }
1591
1592    /// Resolve an async keyed service, extending the propagated resolution chain.
1593    pub fn get_keyed_async<T: ?Sized + Send + Sync + 'static>(
1594        &self,
1595        key: impl Into<ServiceKey> + Send + 'static,
1596    ) -> impl Future<Output = Result<Arc<T>, RdiError>> + Send + 'static {
1597        let provider = Arc::clone(&self.provider);
1598        let chain = self.chain.clone();
1599        let key = key.into();
1600        async move { provider.resolve_async_keyed::<T>(chain, key).await }
1601    }
1602}
1603
1604impl ServiceProvider {
1605    /// Create an [`AsyncFactoryContext`] bound to the **current** async
1606    /// resolution chain.
1607    ///
1608    /// Call this inside an async factory (before spawning) to get a handle that
1609    /// can be cloned into detached tasks. Outside a factory the context starts a
1610    /// fresh chain. Returns `None` only for a provider whose self-reference is
1611    /// not initialized (should not happen for a built provider).
1612    pub fn async_context(&self) -> Option<AsyncFactoryContext> {
1613        Some(AsyncFactoryContext {
1614            provider: self.provider_arc()?,
1615            chain: current_async_chain(),
1616        })
1617    }
1618}
1619
1620impl ScopeFactory for ServiceProvider {
1621    fn create_scope(&self) -> crate::scope::Scope {
1622        // 通过 self_weak 获取 Arc<Self>(new() / new_async() 已初始化 self_weak)。
1623        self.self_weak
1624            .get()
1625            .and_then(|w| w.upgrade())
1626            .map(crate::scope::Scope::new)
1627            .expect("ScopeFactory::create_scope: ServiceProvider self_weak not initialized")
1628    }
1629}
1630
1631impl IServiceResolver for ServiceProvider {
1632    fn get_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
1633        if let Some(entry) = self.entry_by_str(key, None) {
1634            return Some(self.resolve_entry_panicking(entry));
1635        }
1636        // Built-in services resolve through the string path used by the inject
1637        // macros (`#[inject] Arc<dyn ScopeFactory>`, `Arc<ServiceProvider>`).
1638        self.builtin_service_checked(key)
1639    }
1640
1641    fn get_keyed_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>> {
1642        let requested = ServiceKey::from(variant);
1643        self.resolve_keyed_panicking(key, &requested)
1644    }
1645
1646    fn get_keyed_any_key(
1647        &self,
1648        key: &str,
1649        variant: &ServiceKey,
1650    ) -> Option<Arc<dyn Any + Send + Sync>> {
1651        self.resolve_keyed_panicking(key, variant)
1652    }
1653
1654    fn try_get_keyed_any_result_key(
1655        &self,
1656        key: &str,
1657        variant: &ServiceKey,
1658    ) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
1659        self.check_alive()?;
1660        let Some(tid) = self.type_map.get(key) else {
1661            return Ok(None);
1662        };
1663        match self.keyed_entry(*tid, variant) {
1664            Some(KeyedLookup::Exact(entry)) => self.resolve_entry_catching(entry).map(Some),
1665            Some(KeyedLookup::Fallback(entry)) => self
1666                .resolve_any_keyed_entry(entry, variant, false)
1667                .map(Some),
1668            None => Ok(None),
1669        }
1670    }
1671
1672    fn get_keyed_owned_any_key(
1673        &self,
1674        key: &str,
1675        variant: &ServiceKey,
1676    ) -> Option<Arc<dyn Any + Send + Sync>> {
1677        let tid = self.type_map.get(key)?;
1678        match self.keyed_entry(*tid, variant)? {
1679            KeyedLookup::Exact(entry) => {
1680                if entry.lifetime == ServiceLifetime::Singleton {
1681                    return None;
1682                }
1683                self.check_alive().ok()?;
1684                self.create_owned_any_by_entry(entry)
1685            }
1686            KeyedLookup::Fallback(entry) => {
1687                if entry.lifetime == ServiceLifetime::Singleton {
1688                    return None;
1689                }
1690                self.check_alive().ok()?;
1691                self.resolve_any_keyed_entry(entry, variant, true).ok()
1692            }
1693        }
1694    }
1695
1696    fn try_get_keyed_owned_any_key(
1697        &self,
1698        key: &str,
1699        variant: &ServiceKey,
1700    ) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
1701        self.check_alive()?;
1702        let Some(tid) = self.type_map.get(key) else {
1703            return Ok(None);
1704        };
1705        match self.keyed_entry(*tid, variant) {
1706            Some(KeyedLookup::Exact(entry)) => {
1707                if entry.lifetime == ServiceLifetime::Singleton {
1708                    return Ok(None);
1709                }
1710                Ok(self.create_owned_any_by_entry(entry))
1711            }
1712            Some(KeyedLookup::Fallback(entry)) => {
1713                if entry.lifetime == ServiceLifetime::Singleton {
1714                    return Ok(None);
1715                }
1716                self.resolve_any_keyed_entry(entry, variant, true).map(Some)
1717            }
1718            None => Ok(None),
1719        }
1720    }
1721
1722    fn get_by_type_id(&self, tid: TypeId) -> Option<Arc<dyn Any + Send + Sync>> {
1723        if let Some(entry) = self.unkeyed_entry(tid) {
1724            return Some(self.resolve_entry_panicking(entry));
1725        }
1726        self.builtin_service_by_type_id_checked(tid)
1727    }
1728
1729    fn get_keyed_by_type_id(&self, tid: TypeId, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
1730        let requested = ServiceKey::from(key);
1731        match self.keyed_entry(tid, &requested)? {
1732            KeyedLookup::Exact(entry) => Some(self.resolve_entry_panicking(entry)),
1733            KeyedLookup::Fallback(entry) => {
1734                match self.resolve_any_keyed_entry(entry, &requested, false) {
1735                    Ok(value) => Some(value),
1736                    Err(err) => std::panic::panic_any(err),
1737                }
1738            }
1739        }
1740    }
1741
1742    fn get_all_any(&self, key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
1743        self.collect_all_any(key, false)
1744    }
1745
1746    fn get_all_keyed_any(&self, key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
1747        self.collect_all_any(key, true)
1748    }
1749
1750    fn get_all_any_result(&self, key: &str) -> Result<Vec<Arc<dyn Any + Send + Sync>>, RdiError> {
1751        self.check_alive()?;
1752        let Some(tid) = self.type_map.get(key) else {
1753            return Ok(Vec::new());
1754        };
1755        let Some(entries) = self.store.get(tid) else {
1756            return Ok(Vec::new());
1757        };
1758        let mut out = Vec::new();
1759        for entry in entries.iter().filter(|e| e.key.is_none()) {
1760            if entry.is_async() {
1761                // Include already-initialized async singletons (build_async
1762                // initialized them); async scoped/transient services cannot be
1763                // enumerated synchronously.
1764                let ready = entry.lifetime == ServiceLifetime::Singleton
1765                    && self.singleton_cache.get(entry.cache_key).is_some();
1766                if !ready {
1767                    return Err(RdiError::AsyncServiceRequiresGetAsync {
1768                        service: entry.type_name.to_string(),
1769                    });
1770                }
1771            }
1772            out.push(self.resolve_entry_catching(entry)?);
1773        }
1774        Ok(out)
1775    }
1776
1777    fn get_owned_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
1778        let entry = self.entry_by_str(key, None)?;
1779        if entry.lifetime == ServiceLifetime::Singleton {
1780            return None;
1781        }
1782        self.check_alive().ok()?;
1783        self.create_owned_any_by_entry(entry)
1784    }
1785
1786    /// Infallible lookup: reports absence instead of panicking for scope
1787    /// violations / disposal (still returns `Err` for factory failures so
1788    /// callers could distinguish, but [`IServiceResolver::try_get`] maps it to
1789    /// `None`).
1790    fn try_get_any_result(&self, key: &str) -> Result<Option<AnyService>, RdiError> {
1791        self.check_alive()?;
1792        if let Some(entry) = self.entry_by_str(key, None) {
1793            return self.resolve_entry_catching(entry).map(Some);
1794        }
1795        Ok(self.builtin_service_by_name(key))
1796    }
1797
1798    /// Keyed variant of [`IServiceResolver::try_get_any_result`].
1799    fn try_get_keyed_any_result(
1800        &self,
1801        key: &str,
1802        variant: &str,
1803    ) -> Result<Option<AnyService>, RdiError> {
1804        self.check_alive()?;
1805        let requested = ServiceKey::from(variant);
1806        let Some(tid) = self.type_map.get(key) else {
1807            return Ok(None);
1808        };
1809        match self.keyed_entry(*tid, &requested) {
1810            Some(KeyedLookup::Exact(entry)) => self.resolve_entry_catching(entry).map(Some),
1811            Some(KeyedLookup::Fallback(entry)) => self
1812                .resolve_any_keyed_entry(entry, &requested, false)
1813                .map(Some),
1814            None => Ok(None),
1815        }
1816    }
1817
1818    fn get_keyed_owned_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>> {
1819        let requested = ServiceKey::from(variant);
1820        let tid = self.type_map.get(key)?;
1821        match self.keyed_entry(*tid, &requested)? {
1822            KeyedLookup::Exact(entry) => {
1823                if entry.lifetime == ServiceLifetime::Singleton {
1824                    return None;
1825                }
1826                self.check_alive().ok()?;
1827                self.create_owned_any_by_entry(entry)
1828            }
1829            KeyedLookup::Fallback(entry) => {
1830                if entry.lifetime == ServiceLifetime::Singleton {
1831                    return None;
1832                }
1833                self.check_alive().ok()?;
1834                self.resolve_any_keyed_entry(entry, &requested, true).ok()
1835            }
1836        }
1837    }
1838
1839    fn provider_arc(&self) -> Option<Arc<ServiceProvider>> {
1840        self.self_weak.get().and_then(|w| w.upgrade())
1841    }
1842}
1843
1844impl ServiceProvider {
1845    /// Panic-style keyed resolution used by the infallible [`IServiceResolver`]
1846    /// contract; required-resolution APIs catch these typed panics.
1847    fn resolve_keyed_panicking(
1848        &self,
1849        type_name: &str,
1850        requested: &ServiceKey,
1851    ) -> Option<AnyService> {
1852        let tid = *self.type_map.get(type_name)?;
1853        match self.keyed_entry(tid, requested)? {
1854            KeyedLookup::Exact(entry) => Some(self.resolve_entry_panicking(entry)),
1855            KeyedLookup::Fallback(entry) => {
1856                match self.resolve_any_keyed_entry(entry, requested, false) {
1857                    Ok(value) => Some(value),
1858                    Err(err) => std::panic::panic_any(err),
1859                }
1860            }
1861        }
1862    }
1863
1864    fn collect_all_any(&self, key: &str, keyed_only: bool) -> Vec<Arc<dyn Any + Send + Sync>> {
1865        if self.check_alive().is_err() {
1866            return Vec::new();
1867        }
1868        let Some(tid) = self.type_map.get(key) else {
1869            return Vec::new();
1870        };
1871        let Some(entries) = self.store.get(tid) else {
1872            return Vec::new();
1873        };
1874        let mut out = Vec::new();
1875        for entry in entries.iter().filter(|e| {
1876            if keyed_only {
1877                e.key.is_some() && !e.is_any_key()
1878            } else {
1879                e.key.is_none()
1880            }
1881        }) {
1882            match self.resolve_entry_catching(entry) {
1883                Ok(any) => out.push(any),
1884                Err(RdiError::ScopedFromRootProvider { .. }) => {}
1885                Err(err) => std::panic::panic_any(err),
1886            }
1887        }
1888        out
1889    }
1890}
1891
1892#[cfg(test)]
1893mod tests {
1894    use super::ServiceProviderOptions;
1895    use crate::collection::ServiceCollection;
1896    use std::sync::Arc;
1897
1898    #[derive(Debug, PartialEq)]
1899    struct Calc(i32);
1900    #[test]
1901    fn optional_missing() {
1902        let p = ServiceCollection::new().build().unwrap();
1903        assert!(p.get_optional::<i32>().is_none());
1904    }
1905    #[test]
1906    fn get_all_includes_only_unkeyed() {
1907        let p = ServiceCollection::new()
1908            .keyed_singleton("a", |_| Arc::new(Calc(1)))
1909            .keyed_singleton("b", |_| Arc::new(Calc(2)))
1910            .build()
1911            .unwrap();
1912        assert_eq!(p.get_all::<Calc>().len(), 0, "get_all excludes keyed");
1913        assert_eq!(p.get_all_keyed::<Calc>().len(), 2);
1914    }
1915    #[test]
1916    fn later_registration_wins() {
1917        let p = ServiceCollection::new()
1918            .singleton(|_| Arc::new(Calc(1)))
1919            .singleton(|_| Arc::new(Calc(2)))
1920            .build()
1921            .unwrap();
1922        assert_eq!(
1923            p.get::<Calc>().unwrap().0,
1924            2,
1925            "MEDI: last registration wins"
1926        );
1927    }
1928    #[test]
1929    fn root_scoped_is_rejected_by_default() {
1930        let p = ServiceCollection::new()
1931            .scoped(|_| Arc::new(Calc(9)))
1932            .build()
1933            .unwrap();
1934        match p.get::<Calc>() {
1935            Err(crate::error::RdiError::ScopedFromRootProvider { service }) => {
1936                assert!(service.contains("Calc"));
1937            }
1938            other => panic!("expected ScopedFromRootProvider, got {other:?}"),
1939        }
1940        assert!(p.get_optional::<Calc>().is_none());
1941        assert!(p.get_all::<Calc>().is_empty());
1942    }
1943    #[test]
1944    fn lenient_options_restore_root_scope() {
1945        let p = ServiceCollection::new()
1946            .scoped(|_| Arc::new(Calc(9)))
1947            .build_with(ServiceProviderOptions::lenient())
1948            .unwrap();
1949        assert_eq!(p.get::<Calc>().unwrap().0, 9);
1950        assert!(Arc::ptr_eq(
1951            &p.get::<Calc>().unwrap(),
1952            &p.get::<Calc>().unwrap()
1953        ));
1954    }
1955    #[test]
1956    fn scope_resolution_works_and_is_cached() {
1957        let p = ServiceCollection::new()
1958            .scoped(|_| Arc::new(Calc(3)))
1959            .build()
1960            .unwrap();
1961        let s1 = p.scope();
1962        let a = s1.get::<Calc>().unwrap();
1963        let b = s1.get::<Calc>().unwrap();
1964        assert!(Arc::ptr_eq(&a, &b));
1965        let s2 = p.scope();
1966        assert!(!Arc::ptr_eq(&a, &s2.get::<Calc>().unwrap()));
1967    }
1968    #[test]
1969    fn is_registered_reports_lookup_state() {
1970        let p = ServiceCollection::new()
1971            .singleton(|_| Arc::new(Calc(1)))
1972            .keyed_singleton("k", |_| Arc::new(Calc(2)))
1973            .build()
1974            .unwrap();
1975        assert!(p.is_registered::<Calc>());
1976        assert!(p.is_registered::<crate::ServiceProvider>());
1977        assert!(p.is_registered::<dyn crate::ScopeFactory>());
1978        assert!(!p.is_registered::<String>());
1979        assert!(p.is_registered_keyed::<Calc>("k"));
1980        assert!(!p.is_registered_keyed::<Calc>("missing"));
1981    }
1982    #[test]
1983    fn dispose_is_idempotent_and_blocks_resolution() {
1984        let p = ServiceCollection::new()
1985            .singleton(|_| Arc::new(Calc(1)))
1986            .build()
1987            .unwrap();
1988        p.dispose();
1989        p.dispose();
1990        assert!(matches!(
1991            p.get::<Calc>(),
1992            Err(crate::error::RdiError::ProviderDisposed)
1993        ));
1994        assert!(p.get_optional::<Calc>().is_none());
1995        assert!(p.get_all::<Calc>().is_empty());
1996    }
1997    #[test]
1998    fn dispose_releases_singletons_in_reverse_creation_order() {
1999        use std::sync::Mutex;
2000        static DROPS: Mutex<Vec<&'static str>> = Mutex::new(Vec::new());
2001        #[derive(Debug)]
2002        struct Tracked(&'static str);
2003        impl Drop for Tracked {
2004            fn drop(&mut self) {
2005                DROPS.lock().unwrap().push(self.0);
2006            }
2007        }
2008        struct First(#[allow(dead_code)] Arc<Tracked>);
2009        struct Second(#[allow(dead_code)] Arc<Tracked>);
2010
2011        let p = ServiceCollection::new()
2012            .singleton(|_| Arc::new(First(Arc::new(Tracked("first")))))
2013            .singleton(|_| Arc::new(Second(Arc::new(Tracked("second")))))
2014            .build()
2015            .unwrap();
2016        p.dispose();
2017
2018        let drops = DROPS.lock().unwrap().clone();
2019        assert_eq!(
2020            drops,
2021            vec!["second", "first"],
2022            "singletons must be released in reverse creation order"
2023        );
2024    }
2025
2026    #[test]
2027    fn eager_singletons_are_created_in_registration_order() {
2028        use std::sync::Mutex;
2029        static CREATED: Mutex<Vec<&'static str>> = Mutex::new(Vec::new());
2030        #[derive(Debug)]
2031        struct A;
2032        #[derive(Debug)]
2033        struct B;
2034        let p = ServiceCollection::new()
2035            .singleton(|_| {
2036                CREATED.lock().unwrap().push("a");
2037                Arc::new(A)
2038            })
2039            .singleton(|_| {
2040                CREATED.lock().unwrap().push("b");
2041                Arc::new(B)
2042            })
2043            .build()
2044            .unwrap();
2045        assert_eq!(
2046            *CREATED.lock().unwrap(),
2047            vec!["a", "b"],
2048            "eager init must be deterministic (registration order)"
2049        );
2050        drop(p);
2051    }
2052
2053    #[test]
2054    fn async_singleton_panic_becomes_typed_error() {
2055        let result = futures::executor::block_on(
2056            ServiceCollection::new()
2057                .async_singleton::<Calc>(|_| Box::pin(async { panic!("boom") }))
2058                .build_async(),
2059        );
2060        match result {
2061            Ok(_) => panic!("build_async must fail when an async singleton factory panics"),
2062            Err(crate::error::RdiError::FactoryPanic { payload, .. }) => {
2063                assert!(payload.contains("boom"), "payload={payload}");
2064            }
2065            Err(other) => panic!("expected FactoryPanic, got {other:?}"),
2066        }
2067    }
2068}