use crate::cache::AnyService;
use crate::entry::IServiceResolver;
use crate::error::RdiError;
use crate::provider::ServiceProvider;
use std::any::{Any, TypeId};
use std::sync::{Arc, Weak};
pub trait ScopeFactory: Send + Sync {
fn create_scope(&self) -> Scope;
}
pub(crate) struct BuiltinScopeFactory {
provider: Weak<ServiceProvider>,
}
impl BuiltinScopeFactory {
pub(crate) fn new(provider: Weak<ServiceProvider>) -> Self {
Self { provider }
}
}
impl ScopeFactory for BuiltinScopeFactory {
fn create_scope(&self) -> Scope {
self.provider.upgrade().map(Scope::new).expect(
"ScopeFactory::create_scope: ServiceProvider has been dropped; \
the DI container must outlive services created from it",
)
}
}
pub(crate) fn builtin_scope_factory(provider: Weak<ServiceProvider>) -> Arc<dyn ScopeFactory> {
Arc::new(BuiltinScopeFactory::new(provider))
}
pub(crate) fn builtin_scope_factory_any(factory: Arc<dyn ScopeFactory>) -> AnyService {
Arc::new(factory) as AnyService
}
pub struct Scope {
scope_provider: Arc<ServiceProvider>,
}
impl Scope {
pub(crate) fn new(parent: Arc<ServiceProvider>) -> Self {
Self {
scope_provider: ServiceProvider::new_scope(&parent),
}
}
pub fn scope_provider(&self) -> &Arc<ServiceProvider> {
&self.scope_provider
}
pub fn dispose(&self) {
self.scope_provider.dispose();
}
pub fn get<T: ?Sized + Send + Sync + 'static>(&self) -> Result<Arc<T>, RdiError> {
self.scope_provider.get::<T>()
}
pub fn get_optional<T: ?Sized + Send + Sync + 'static>(&self) -> Option<Arc<T>> {
self.scope_provider.get_optional::<T>()
}
pub fn get_keyed<T: ?Sized + Send + Sync + 'static>(
&self,
key: impl Into<crate::key::ServiceKey>,
) -> Result<Arc<T>, RdiError> {
self.scope_provider.get_keyed::<T>(key)
}
pub fn get_async<T: ?Sized + Send + Sync + 'static>(
&self,
) -> impl std::future::Future<Output = Result<Arc<T>, RdiError>> + Send + '_ {
self.scope_provider.get_async::<T>()
}
pub fn get_keyed_async<T: ?Sized + Send + Sync + 'static>(
&self,
key: impl Into<crate::key::ServiceKey> + Send,
) -> impl std::future::Future<Output = Result<Arc<T>, RdiError>> + Send + '_ {
self.scope_provider.get_keyed_async::<T>(key)
}
pub fn get_owned<T: Send + Sync + 'static>(&self) -> Result<T, RdiError> {
self.scope_provider.get_owned::<T>()
}
pub fn try_get_owned<T: Send + Sync + 'static>(&self) -> Option<T> {
self.scope_provider.try_get_owned::<T>()
}
pub fn get_keyed_owned<T: Send + Sync + 'static>(
&self,
key: impl Into<crate::key::ServiceKey>,
) -> Result<T, RdiError> {
self.scope_provider.get_keyed_owned::<T>(key)
}
pub fn try_get_keyed_owned<T: Send + Sync + 'static>(
&self,
key: impl Into<crate::key::ServiceKey>,
) -> Option<T> {
self.scope_provider.try_get_keyed_owned::<T>(key)
}
pub fn get_all<T: ?Sized + Send + Sync + 'static>(&self) -> Vec<Arc<T>> {
self.scope_provider.get_all::<T>()
}
pub fn get_all_keyed<T: ?Sized + Send + Sync + 'static>(&self) -> Vec<Arc<T>> {
self.scope_provider.get_all_keyed::<T>()
}
pub fn is_registered<T: ?Sized + Send + Sync + 'static>(&self) -> bool {
self.scope_provider.is_registered::<T>()
}
pub fn is_registered_keyed<T: ?Sized + Send + Sync + 'static>(
&self,
key: impl Into<crate::key::ServiceKey>,
) -> bool {
self.scope_provider.is_registered_keyed::<T>(key)
}
pub fn async_context(&self) -> Option<crate::AsyncFactoryContext> {
self.scope_provider.async_context()
}
pub fn get_named_any(&self, name: &str) -> Option<Arc<dyn Any + Send + Sync>> {
self.scope_provider.get_named_any(name)
}
pub fn rdi_register_named(&self, name: &str, service: Arc<dyn Any + Send + Sync>) {
self.scope_provider.rdi_register_named(name, service);
}
pub fn rdi_remove_named(&self, name: &str) {
self.scope_provider.rdi_remove_named(name);
}
}
impl Drop for Scope {
fn drop(&mut self) {
self.dispose();
}
}
impl IServiceResolver for Scope {
fn get_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
self.scope_provider.get_any(key)
}
fn get_keyed_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>> {
self.scope_provider.get_keyed_any(key, variant)
}
fn get_keyed_any_key(
&self,
key: &str,
variant: &crate::key::ServiceKey,
) -> Option<Arc<dyn Any + Send + Sync>> {
self.scope_provider.get_keyed_any_key(key, variant)
}
fn try_get_keyed_any_result_key(
&self,
key: &str,
variant: &crate::key::ServiceKey,
) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
self.scope_provider
.try_get_keyed_any_result_key(key, variant)
}
fn get_keyed_owned_any_key(
&self,
key: &str,
variant: &crate::key::ServiceKey,
) -> Option<Arc<dyn Any + Send + Sync>> {
self.scope_provider.get_keyed_owned_any_key(key, variant)
}
fn try_get_keyed_owned_any_key(
&self,
key: &str,
variant: &crate::key::ServiceKey,
) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
self.scope_provider
.try_get_keyed_owned_any_key(key, variant)
}
fn get_by_type_id(&self, tid: TypeId) -> Option<Arc<dyn Any + Send + Sync>> {
self.scope_provider.get_by_type_id(tid)
}
fn get_keyed_by_type_id(&self, tid: TypeId, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
self.scope_provider.get_keyed_by_type_id(tid, key)
}
fn get_all_any(&self, key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
self.scope_provider.get_all_any(key)
}
fn get_all_keyed_any(&self, key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
self.scope_provider.get_all_keyed_any(key)
}
fn get_all_any_result(&self, key: &str) -> Result<Vec<Arc<dyn Any + Send + Sync>>, RdiError> {
self.scope_provider.get_all_any_result(key)
}
fn get_owned_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
self.scope_provider.get_owned_any(key)
}
fn try_get_any_result(
&self,
key: &str,
) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
self.scope_provider.try_get_any_result(key)
}
fn try_get_keyed_any_result(
&self,
key: &str,
variant: &str,
) -> Result<Option<Arc<dyn Any + Send + Sync>>, RdiError> {
self.scope_provider.try_get_keyed_any_result(key, variant)
}
fn get_keyed_owned_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>> {
self.scope_provider.get_keyed_owned_any(key, variant)
}
fn provider_arc(&self) -> Option<Arc<ServiceProvider>> {
Some(Arc::clone(&self.scope_provider))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::collection::ServiceCollection;
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug, PartialEq)]
struct Sd(u64);
#[test]
fn scoped_cached_per_scope() {
static NXT: AtomicU64 = AtomicU64::new(0);
let p = ServiceCollection::new()
.scoped(|_| Arc::new(Sd(NXT.fetch_add(1, Ordering::SeqCst))))
.build()
.unwrap();
let s1 = p.scope();
let a = s1.get::<Sd>().unwrap();
let b = s1.get::<Sd>().unwrap();
assert_eq!(a.0, b.0);
let s2 = p.scope();
let c = s2.get::<Sd>().unwrap();
assert_ne!(a.0, c.0);
}
#[test]
fn disposed_scope_rejects_resolution() {
let p = ServiceCollection::new()
.scoped(|_| Arc::new(Sd(1)))
.build()
.unwrap();
let scope = p.scope();
assert!(scope.get::<Sd>().is_ok());
scope.dispose();
assert!(matches!(
scope.get::<Sd>(),
Err(crate::error::RdiError::ScopeDisposed)
));
assert!(scope.get_optional::<Sd>().is_none());
}
#[test]
fn nested_transient_chain_scoped_binds_to_child_scope() {
static CORE_CALLS: AtomicU64 = AtomicU64::new(0);
struct ScopedCore(u64);
struct MidTransient {
core: Arc<ScopedCore>,
}
struct OuterTransient {
mid: Arc<MidTransient>,
}
let provider = ServiceCollection::new()
.scoped(|_| {
let n = CORE_CALLS.fetch_add(1, Ordering::SeqCst);
Arc::new(ScopedCore(n))
})
.transient(|r| {
let any = r.get_any(std::any::type_name::<ScopedCore>()).unwrap();
let outer = any.downcast::<Arc<ScopedCore>>().unwrap();
let core: Arc<ScopedCore> = Arc::clone(&*outer);
Arc::new(MidTransient { core })
})
.transient(|r| {
let any = r.get_any(std::any::type_name::<MidTransient>()).unwrap();
let outer = any.downcast::<Arc<MidTransient>>().unwrap();
let mid: Arc<MidTransient> = Arc::clone(&*outer);
Arc::new(OuterTransient { mid })
})
.build()
.unwrap();
let scope1 = provider.scope();
let o1: Arc<OuterTransient> = scope1.get().unwrap();
assert_eq!(o1.mid.core.0, 0, "scope1 的 ScopedCore 应为序号 0");
let o1b: Arc<OuterTransient> = scope1.get().unwrap();
assert_eq!(o1b.mid.core.0, 0, "scope1 应复用同一 ScopedCore");
assert!(
Arc::ptr_eq(&o1.mid.core, &o1b.mid.core),
"scope1 内 ScopedCore 必须复用"
);
let scope2 = provider.scope();
let o2: Arc<OuterTransient> = scope2.get().unwrap();
assert_eq!(o2.mid.core.0, 1, "scope2 的 ScopedCore 应为序号 1");
assert!(
!Arc::ptr_eq(&o1.mid.core, &o2.mid.core),
"不同 scope 的 ScopedCore 必须独立"
);
assert!(matches!(
provider.get::<ScopedCore>(),
Err(crate::error::RdiError::ScopedFromRootProvider { .. })
));
assert_eq!(
CORE_CALLS.load(Ordering::SeqCst),
2,
"ScopedCore 工厂应执行 2 次(两个子 scope 各一次)"
);
}
}