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_by_type_id(&self, tid: TypeId) -> Option<Arc<dyn Any + Send + Sync>> {
1647        if let Some(entry) = self.unkeyed_entry(tid) {
1648            return Some(self.resolve_entry_panicking(entry));
1649        }
1650        self.builtin_service_by_type_id_checked(tid)
1651    }
1652
1653    fn get_keyed_by_type_id(&self, tid: TypeId, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
1654        let requested = ServiceKey::from(key);
1655        match self.keyed_entry(tid, &requested)? {
1656            KeyedLookup::Exact(entry) => Some(self.resolve_entry_panicking(entry)),
1657            KeyedLookup::Fallback(entry) => {
1658                match self.resolve_any_keyed_entry(entry, &requested, false) {
1659                    Ok(value) => Some(value),
1660                    Err(err) => std::panic::panic_any(err),
1661                }
1662            }
1663        }
1664    }
1665
1666    fn get_all_any(&self, key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
1667        self.collect_all_any(key, false)
1668    }
1669
1670    fn get_all_keyed_any(&self, key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
1671        self.collect_all_any(key, true)
1672    }
1673
1674    fn get_all_any_result(&self, key: &str) -> Result<Vec<Arc<dyn Any + Send + Sync>>, RdiError> {
1675        self.check_alive()?;
1676        let Some(tid) = self.type_map.get(key) else {
1677            return Ok(Vec::new());
1678        };
1679        let Some(entries) = self.store.get(tid) else {
1680            return Ok(Vec::new());
1681        };
1682        let mut out = Vec::new();
1683        for entry in entries.iter().filter(|e| e.key.is_none()) {
1684            if entry.is_async() {
1685                // Include already-initialized async singletons (build_async
1686                // initialized them); async scoped/transient services cannot be
1687                // enumerated synchronously.
1688                let ready = entry.lifetime == ServiceLifetime::Singleton
1689                    && self.singleton_cache.get(entry.cache_key).is_some();
1690                if !ready {
1691                    return Err(RdiError::AsyncServiceRequiresGetAsync {
1692                        service: entry.type_name.to_string(),
1693                    });
1694                }
1695            }
1696            out.push(self.resolve_entry_catching(entry)?);
1697        }
1698        Ok(out)
1699    }
1700
1701    fn get_owned_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
1702        let entry = self.entry_by_str(key, None)?;
1703        if entry.lifetime == ServiceLifetime::Singleton {
1704            return None;
1705        }
1706        self.check_alive().ok()?;
1707        self.create_owned_any_by_entry(entry)
1708    }
1709
1710    /// Infallible lookup: reports absence instead of panicking for scope
1711    /// violations / disposal (still returns `Err` for factory failures so
1712    /// callers could distinguish, but [`IServiceResolver::try_get`] maps it to
1713    /// `None`).
1714    fn try_get_any_result(&self, key: &str) -> Result<Option<AnyService>, RdiError> {
1715        self.check_alive()?;
1716        if let Some(entry) = self.entry_by_str(key, None) {
1717            return self.resolve_entry_catching(entry).map(Some);
1718        }
1719        Ok(self.builtin_service_by_name(key))
1720    }
1721
1722    /// Keyed variant of [`IServiceResolver::try_get_any_result`].
1723    fn try_get_keyed_any_result(
1724        &self,
1725        key: &str,
1726        variant: &str,
1727    ) -> Result<Option<AnyService>, RdiError> {
1728        self.check_alive()?;
1729        let requested = ServiceKey::from(variant);
1730        let Some(tid) = self.type_map.get(key) else {
1731            return Ok(None);
1732        };
1733        match self.keyed_entry(*tid, &requested) {
1734            Some(KeyedLookup::Exact(entry)) => self.resolve_entry_catching(entry).map(Some),
1735            Some(KeyedLookup::Fallback(entry)) => self
1736                .resolve_any_keyed_entry(entry, &requested, false)
1737                .map(Some),
1738            None => Ok(None),
1739        }
1740    }
1741
1742    fn get_keyed_owned_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>> {
1743        let requested = ServiceKey::from(variant);
1744        let tid = self.type_map.get(key)?;
1745        match self.keyed_entry(*tid, &requested)? {
1746            KeyedLookup::Exact(entry) => {
1747                if entry.lifetime == ServiceLifetime::Singleton {
1748                    return None;
1749                }
1750                self.check_alive().ok()?;
1751                self.create_owned_any_by_entry(entry)
1752            }
1753            KeyedLookup::Fallback(entry) => {
1754                if entry.lifetime == ServiceLifetime::Singleton {
1755                    return None;
1756                }
1757                self.check_alive().ok()?;
1758                self.resolve_any_keyed_entry(entry, &requested, true).ok()
1759            }
1760        }
1761    }
1762
1763    fn provider_arc(&self) -> Option<Arc<ServiceProvider>> {
1764        self.self_weak.get().and_then(|w| w.upgrade())
1765    }
1766}
1767
1768impl ServiceProvider {
1769    /// Panic-style keyed resolution used by the infallible [`IServiceResolver`]
1770    /// contract; required-resolution APIs catch these typed panics.
1771    fn resolve_keyed_panicking(
1772        &self,
1773        type_name: &str,
1774        requested: &ServiceKey,
1775    ) -> Option<AnyService> {
1776        let tid = *self.type_map.get(type_name)?;
1777        match self.keyed_entry(tid, requested)? {
1778            KeyedLookup::Exact(entry) => Some(self.resolve_entry_panicking(entry)),
1779            KeyedLookup::Fallback(entry) => {
1780                match self.resolve_any_keyed_entry(entry, requested, false) {
1781                    Ok(value) => Some(value),
1782                    Err(err) => std::panic::panic_any(err),
1783                }
1784            }
1785        }
1786    }
1787
1788    fn collect_all_any(&self, key: &str, keyed_only: bool) -> Vec<Arc<dyn Any + Send + Sync>> {
1789        if self.check_alive().is_err() {
1790            return Vec::new();
1791        }
1792        let Some(tid) = self.type_map.get(key) else {
1793            return Vec::new();
1794        };
1795        let Some(entries) = self.store.get(tid) else {
1796            return Vec::new();
1797        };
1798        let mut out = Vec::new();
1799        for entry in entries.iter().filter(|e| {
1800            if keyed_only {
1801                e.key.is_some() && !e.is_any_key()
1802            } else {
1803                e.key.is_none()
1804            }
1805        }) {
1806            match self.resolve_entry_catching(entry) {
1807                Ok(any) => out.push(any),
1808                Err(RdiError::ScopedFromRootProvider { .. }) => {}
1809                Err(err) => std::panic::panic_any(err),
1810            }
1811        }
1812        out
1813    }
1814}
1815
1816#[cfg(test)]
1817mod tests {
1818    use super::ServiceProviderOptions;
1819    use crate::collection::ServiceCollection;
1820    use std::sync::Arc;
1821
1822    #[derive(Debug, PartialEq)]
1823    struct Calc(i32);
1824    #[test]
1825    fn optional_missing() {
1826        let p = ServiceCollection::new().build().unwrap();
1827        assert!(p.get_optional::<i32>().is_none());
1828    }
1829    #[test]
1830    fn get_all_includes_only_unkeyed() {
1831        let p = ServiceCollection::new()
1832            .keyed_singleton("a", |_| Arc::new(Calc(1)))
1833            .keyed_singleton("b", |_| Arc::new(Calc(2)))
1834            .build()
1835            .unwrap();
1836        assert_eq!(p.get_all::<Calc>().len(), 0, "get_all excludes keyed");
1837        assert_eq!(p.get_all_keyed::<Calc>().len(), 2);
1838    }
1839    #[test]
1840    fn later_registration_wins() {
1841        let p = ServiceCollection::new()
1842            .singleton(|_| Arc::new(Calc(1)))
1843            .singleton(|_| Arc::new(Calc(2)))
1844            .build()
1845            .unwrap();
1846        assert_eq!(
1847            p.get::<Calc>().unwrap().0,
1848            2,
1849            "MEDI: last registration wins"
1850        );
1851    }
1852    #[test]
1853    fn root_scoped_is_rejected_by_default() {
1854        let p = ServiceCollection::new()
1855            .scoped(|_| Arc::new(Calc(9)))
1856            .build()
1857            .unwrap();
1858        match p.get::<Calc>() {
1859            Err(crate::error::RdiError::ScopedFromRootProvider { service }) => {
1860                assert!(service.contains("Calc"));
1861            }
1862            other => panic!("expected ScopedFromRootProvider, got {other:?}"),
1863        }
1864        assert!(p.get_optional::<Calc>().is_none());
1865        assert!(p.get_all::<Calc>().is_empty());
1866    }
1867    #[test]
1868    fn lenient_options_restore_root_scope() {
1869        let p = ServiceCollection::new()
1870            .scoped(|_| Arc::new(Calc(9)))
1871            .build_with(ServiceProviderOptions::lenient())
1872            .unwrap();
1873        assert_eq!(p.get::<Calc>().unwrap().0, 9);
1874        assert!(Arc::ptr_eq(
1875            &p.get::<Calc>().unwrap(),
1876            &p.get::<Calc>().unwrap()
1877        ));
1878    }
1879    #[test]
1880    fn scope_resolution_works_and_is_cached() {
1881        let p = ServiceCollection::new()
1882            .scoped(|_| Arc::new(Calc(3)))
1883            .build()
1884            .unwrap();
1885        let s1 = p.scope();
1886        let a = s1.get::<Calc>().unwrap();
1887        let b = s1.get::<Calc>().unwrap();
1888        assert!(Arc::ptr_eq(&a, &b));
1889        let s2 = p.scope();
1890        assert!(!Arc::ptr_eq(&a, &s2.get::<Calc>().unwrap()));
1891    }
1892    #[test]
1893    fn is_registered_reports_lookup_state() {
1894        let p = ServiceCollection::new()
1895            .singleton(|_| Arc::new(Calc(1)))
1896            .keyed_singleton("k", |_| Arc::new(Calc(2)))
1897            .build()
1898            .unwrap();
1899        assert!(p.is_registered::<Calc>());
1900        assert!(p.is_registered::<crate::ServiceProvider>());
1901        assert!(p.is_registered::<dyn crate::ScopeFactory>());
1902        assert!(!p.is_registered::<String>());
1903        assert!(p.is_registered_keyed::<Calc>("k"));
1904        assert!(!p.is_registered_keyed::<Calc>("missing"));
1905    }
1906    #[test]
1907    fn dispose_is_idempotent_and_blocks_resolution() {
1908        let p = ServiceCollection::new()
1909            .singleton(|_| Arc::new(Calc(1)))
1910            .build()
1911            .unwrap();
1912        p.dispose();
1913        p.dispose();
1914        assert!(matches!(
1915            p.get::<Calc>(),
1916            Err(crate::error::RdiError::ProviderDisposed)
1917        ));
1918        assert!(p.get_optional::<Calc>().is_none());
1919        assert!(p.get_all::<Calc>().is_empty());
1920    }
1921    #[test]
1922    fn dispose_releases_singletons_in_reverse_creation_order() {
1923        use std::sync::Mutex;
1924        static DROPS: Mutex<Vec<&'static str>> = Mutex::new(Vec::new());
1925        #[derive(Debug)]
1926        struct Tracked(&'static str);
1927        impl Drop for Tracked {
1928            fn drop(&mut self) {
1929                DROPS.lock().unwrap().push(self.0);
1930            }
1931        }
1932        struct First(#[allow(dead_code)] Arc<Tracked>);
1933        struct Second(#[allow(dead_code)] Arc<Tracked>);
1934
1935        let p = ServiceCollection::new()
1936            .singleton(|_| Arc::new(First(Arc::new(Tracked("first")))))
1937            .singleton(|_| Arc::new(Second(Arc::new(Tracked("second")))))
1938            .build()
1939            .unwrap();
1940        p.dispose();
1941
1942        let drops = DROPS.lock().unwrap().clone();
1943        assert_eq!(
1944            drops,
1945            vec!["second", "first"],
1946            "singletons must be released in reverse creation order"
1947        );
1948    }
1949
1950    #[test]
1951    fn eager_singletons_are_created_in_registration_order() {
1952        use std::sync::Mutex;
1953        static CREATED: Mutex<Vec<&'static str>> = Mutex::new(Vec::new());
1954        #[derive(Debug)]
1955        struct A;
1956        #[derive(Debug)]
1957        struct B;
1958        let p = ServiceCollection::new()
1959            .singleton(|_| {
1960                CREATED.lock().unwrap().push("a");
1961                Arc::new(A)
1962            })
1963            .singleton(|_| {
1964                CREATED.lock().unwrap().push("b");
1965                Arc::new(B)
1966            })
1967            .build()
1968            .unwrap();
1969        assert_eq!(
1970            *CREATED.lock().unwrap(),
1971            vec!["a", "b"],
1972            "eager init must be deterministic (registration order)"
1973        );
1974        drop(p);
1975    }
1976
1977    #[test]
1978    fn async_singleton_panic_becomes_typed_error() {
1979        let result = futures::executor::block_on(
1980            ServiceCollection::new()
1981                .async_singleton::<Calc>(|_| Box::pin(async { panic!("boom") }))
1982                .build_async(),
1983        );
1984        match result {
1985            Ok(_) => panic!("build_async must fail when an async singleton factory panics"),
1986            Err(crate::error::RdiError::FactoryPanic { payload, .. }) => {
1987                assert!(payload.contains("boom"), "payload={payload}");
1988            }
1989            Err(other) => panic!("expected FactoryPanic, got {other:?}"),
1990        }
1991    }
1992}