Skip to main content

rust_dix/
scope.rs

1use crate::entry::IServiceResolver;
2use crate::error::RdiError;
3use crate::provider::ServiceProvider;
4use std::any::{Any, TypeId};
5use std::sync::Arc;
6
7/// 工厂 trait:创建独立 scope。可注入 singleton 服务按需创建 scope。
8///
9/// 典型场景:后台 worker singleton 需要按请求创建独立 scope,
10/// 在 scope 内解析 Scoped 服务(如 request-scoped DbContext)。
11///
12/// `ServiceProvider` 实现此 trait,通过 [`ServiceProvider::scope`] 创建 scope。
13/// 注册方式:`provider.singleton::<dyn ScopeFactory>(move |_| provider.clone() as Arc<dyn ScopeFactory>)`。
14pub trait ScopeFactory: Send + Sync {
15    /// 创建一个独立 scope,共享 root 的 store/singleton_cache/named,
16    /// 独立 scoped_cache 和 disposables。
17    fn create_scope(&self) -> Scope;
18}
19
20/// A scoped service provider — created via [`ServiceProvider::scope`].
21///
22/// 内部持有一个 scope 范围的 `ServiceProvider`(由 `ServiceProvider::new_scope` 派生),
23/// 共享 root 的 store/singleton_cache/named,独立 `root_scoped_cache`。
24///
25/// 所有解析方法委托给内嵌的 `scope_provider`,确保:
26/// - Scoped 服务在 scope 内缓存复用(命中 `scope_provider.root_scoped_cache`)。
27/// - Transient 依赖通过 `scope_provider` 解析,正确绑定到当前 scope(MEDI 语义)。
28/// - `#[inject(provider)]` 字段拿到的是 scope 范围的 provider,经该字段解析 Scoped
29///   服务命中 scope 缓存——不泄漏到 root。
30///
31/// # 资源释放
32///
33/// 当 `Scope` 被 drop 时,其内部的 `scope_provider` 被释放,scoped 缓存中的
34/// `Arc<T>` 实例随之释放。若服务需要确定性资源释放(如关闭数据库连接),
35/// 可在工厂内通过 [`ServiceProvider::register_disposable`] 注册清理闭包,
36/// 并调用 [`Scope::dispose`] 在 scope 结束前执行显式清理。
37pub struct Scope {
38    scope_provider: Arc<ServiceProvider>,
39}
40
41impl Scope {
42    pub(crate) fn new(parent: Arc<ServiceProvider>) -> Self {
43        Self {
44            scope_provider: ServiceProvider::new_scope(&parent),
45        }
46    }
47
48    /// Returns the scope-local `ServiceProvider` backing this scope.
49    ///
50    /// Exposed for advanced use cases (e.g. passing the scope provider to
51    /// `ServiceProviderWrapper`). Most callers should use [`Self::get`] /
52    /// [`Self::get_owned`] directly.
53    pub fn scope_provider(&self) -> &Arc<ServiceProvider> {
54        &self.scope_provider
55    }
56
57    /// Explicitly dispose all scoped services registered via [`ServiceProvider::register_disposable`].
58    ///
59    /// Services that need deterministic cleanup (e.g., database connections,
60    /// file handles) should register a cleanup closure in their factory:
61    ///
62    /// ```ignore
63    /// .scoped(|r| {
64    ///     let conn = Arc::new(DbConnection::connect());
65    ///     let conn_clone = Arc::clone(&conn);
66    ///     r.provider_arc().unwrap().register_disposable(Box::new(move || {
67    ///         conn_clone.close();
68    ///     }));
69    ///     conn
70    /// })
71    /// ```
72    ///
73    /// Called automatically on `Drop` as a safety net. If `Drop` runs while a
74    /// concurrent resolution is in progress (factory holding the disposable
75    /// lock), disposal is skipped — some cleanups may be delayed or dropped.
76    /// For deterministic cleanup, call this explicitly before the scope goes
77    /// out of scope.
78    pub fn dispose(&self) {
79        self.scope_provider.dispose_scoped();
80    }
81
82    pub fn get<T: ?Sized + Send + Sync + 'static>(&self) -> Result<Arc<T>, RdiError> {
83        self.scope_provider.get::<T>()
84    }
85
86    pub fn get_optional<T: ?Sized + Send + Sync + 'static>(&self) -> Option<Arc<T>> {
87        self.scope_provider.get_optional::<T>()
88    }
89
90    pub fn get_keyed<T: ?Sized + Send + Sync + 'static>(&self, key: &str) -> Result<Arc<T>, RdiError> {
91        self.scope_provider.get_keyed::<T>(key)
92    }
93
94    /// Resolve an async service (transient or scoped) within this scope.
95    pub async fn get_async<T: ?Sized + Send + Sync + 'static>(
96        &self,
97    ) -> Result<Arc<T>, RdiError> {
98        self.scope_provider.get_async::<T>().await
99    }
100
101    /// Resolve an async keyed service within this scope.
102    pub async fn get_keyed_async<T: ?Sized + Send + Sync + 'static>(
103        &self,
104        key: &str,
105    ) -> Result<Arc<T>, RdiError> {
106        self.scope_provider.get_keyed_async::<T>(key).await
107    }
108
109    /// Resolve owned `T` within this scope (bypass cache, fresh each call).
110    /// Returns `Err` if not registered or the service is Singleton.
111    pub fn get_owned<T: Send + Sync + 'static>(&self) -> Result<T, RdiError> {
112        self.scope_provider.get_owned::<T>()
113    }
114
115    /// Resolve owned `T`, returning `None` if not registered or Singleton.
116    pub fn try_get_owned<T: Send + Sync + 'static>(&self) -> Option<T> {
117        self.scope_provider.try_get_owned::<T>()
118    }
119
120    /// Resolve owned keyed `T`.
121    /// Returns `Err` if not found or Singleton.
122    pub fn get_keyed_owned<T: Send + Sync + 'static>(&self, key: &str) -> Result<T, RdiError> {
123        self.scope_provider.get_keyed_owned::<T>(key)
124    }
125
126    /// Resolve owned keyed `T`, returning `None` if not found or Singleton.
127    pub fn try_get_keyed_owned<T: Send + Sync + 'static>(&self, key: &str) -> Option<T> {
128        self.scope_provider.try_get_keyed_owned::<T>(key)
129    }
130
131    pub fn get_all<T: ?Sized + Send + Sync + 'static>(&self) -> Vec<Arc<T>> {
132        self.scope_provider.get_all::<T>()
133    }
134
135    pub fn get_named_any(&self, name: &str) -> Option<Arc<dyn Any + Send + Sync>> {
136        self.scope_provider.get_named_any(name)
137    }
138
139    /// Register a named service (for [`IProvider`](crate::bridge::IProvider) trait).
140    pub fn rdi_register_named(&self, name: &str, service: Arc<dyn Any + Send + Sync>) {
141        self.scope_provider.rdi_register_named(name, service);
142    }
143
144    /// Remove a named service (for [`IProvider`](crate::bridge::IProvider) trait).
145    pub fn rdi_remove_named(&self, name: &str) {
146        self.scope_provider.rdi_remove_named(name);
147    }
148}
149
150impl Drop for Scope {
151    fn drop(&mut self) {
152        self.dispose();
153    }
154}
155
156impl IServiceResolver for Scope {
157    fn get_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
158        self.scope_provider.get_any(key)
159    }
160    fn get_keyed_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>> {
161        self.scope_provider.get_keyed_any(key, variant)
162    }
163    fn get_by_type_id(&self, tid: TypeId) -> Option<Arc<dyn Any + Send + Sync>> {
164        self.scope_provider.get_by_type_id(tid)
165    }
166    fn get_keyed_by_type_id(
167        &self,
168        tid: TypeId,
169        key: &str,
170    ) -> Option<Arc<dyn Any + Send + Sync>> {
171        self.scope_provider.get_keyed_by_type_id(tid, key)
172    }
173    fn get_all_any(&self, key: &str) -> Vec<Arc<dyn Any + Send + Sync>> {
174        self.scope_provider.get_all_any(key)
175    }
176    fn get_owned_any(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
177        self.scope_provider.get_owned_any(key)
178    }
179    fn get_keyed_owned_any(&self, key: &str, variant: &str) -> Option<Arc<dyn Any + Send + Sync>> {
180        self.scope_provider.get_keyed_owned_any(key, variant)
181    }
182    fn provider_arc(&self) -> Option<Arc<ServiceProvider>> {
183        // 返回 scope 范围的 provider(不是 root),符合 MEDI 语义:
184        // 在 scope 内解析的服务经 #[inject(provider)] 拿到此 provider,
185        // 后续 get::<ScopedX>() 命中 scope 自己的 root_scoped_cache。
186        Some(Arc::clone(&self.scope_provider))
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193    use crate::collection::ServiceCollection;
194    use std::sync::atomic::{AtomicU64, Ordering};
195    #[derive(Debug, PartialEq)]
196    struct Sd(u64);
197    #[test]
198    fn scoped_cached_per_scope() {
199        static NXT: AtomicU64 = AtomicU64::new(0);
200        let p = ServiceCollection::new()
201            .scoped(|_| Arc::new(Sd(NXT.fetch_add(1, Ordering::SeqCst))))
202            .build()
203            .unwrap();
204        let s1 = p.scope();
205        let a = s1.get::<Sd>().unwrap();
206        let b = s1.get::<Sd>().unwrap();
207        assert_eq!(a.0, b.0);
208        let s2 = p.scope();
209        let c = s2.get::<Sd>().unwrap();
210        assert_ne!(a.0, c.0);
211    }
212
213    #[test]
214    fn nested_transient_chain_scoped_binds_to_child_scope() {
215        // 边界场景:双层嵌套 Transient 依赖链 → Scoped
216        //   OuterTransient → MidTransient → ScopedCore
217        // 验证最内层 ScopedCore 在子 scope 内解析(scope.scoped_cache),
218        // 不回退到根 root_scoped_cache。
219        // 严格性:任一层 Transient 若错误用 self.parent 解析依赖,
220        // ScopedCore 都会泄漏到根,三 scope 序号会塌缩为相同值。
221        static CORE_CALLS: AtomicU64 = AtomicU64::new(0);
222
223        struct ScopedCore(u64);
224        struct MidTransient {
225            core: Arc<ScopedCore>,
226        }
227        struct OuterTransient {
228            mid: Arc<MidTransient>,
229        }
230
231        let provider = ServiceCollection::new()
232            .scoped(|_| {
233                let n = CORE_CALLS.fetch_add(1, Ordering::SeqCst);
234                Arc::new(ScopedCore(n))
235            })
236            .transient(|r| {
237                let any = r.get_any(std::any::type_name::<ScopedCore>()).unwrap();
238                let outer = any.downcast::<Arc<ScopedCore>>().unwrap();
239                let core: Arc<ScopedCore> = Arc::clone(&*outer);
240                Arc::new(MidTransient { core })
241            })
242            .transient(|r| {
243                let any = r.get_any(std::any::type_name::<MidTransient>()).unwrap();
244                let outer = any.downcast::<Arc<MidTransient>>().unwrap();
245                let mid: Arc<MidTransient> = Arc::clone(&*outer);
246                Arc::new(OuterTransient { mid })
247            })
248            .build()
249            .unwrap();
250
251        // scope1:首次解析 OuterTransient,ScopedCore 工厂执行(序号 0)。
252        let scope1 = provider.scope();
253        let o1: Arc<OuterTransient> = scope1.get().unwrap();
254        assert_eq!(o1.mid.core.0, 0, "scope1 的 ScopedCore 应为序号 0");
255
256        // scope1 内再次解析:OuterTransient/MidTransient 每次新建(Transient),
257        // 但 ScopedCore 应复用 scope1.scoped_cache(序号仍 0)。
258        let o1b: Arc<OuterTransient> = scope1.get().unwrap();
259        assert_eq!(o1b.mid.core.0, 0, "scope1 应复用同一 ScopedCore");
260        assert!(
261            Arc::ptr_eq(&o1.mid.core, &o1b.mid.core),
262            "scope1 内 ScopedCore 必须复用"
263        );
264
265        // scope2:独立子 scope,ScopedCore 工厂再次执行(序号 1)。
266        let scope2 = provider.scope();
267        let o2: Arc<OuterTransient> = scope2.get().unwrap();
268        assert_eq!(o2.mid.core.0, 1, "scope2 的 ScopedCore 应为序号 1");
269        assert!(
270            !Arc::ptr_eq(&o1.mid.core, &o2.mid.core),
271            "不同 scope 的 ScopedCore 必须独立"
272        );
273
274        // 根 scope:第三个独立实例(序号 2),证明子 scope 未泄漏到根。
275        let root_c: Arc<ScopedCore> = provider.get().unwrap();
276        assert_eq!(root_c.0, 2, "根 scope 的 ScopedCore 应为序号 2");
277        assert!(!Arc::ptr_eq(&o1.mid.core, &root_c));
278        assert!(!Arc::ptr_eq(&o2.mid.core, &root_c));
279
280        // 全程工厂只执行 3 次(scope1、scope2、根各一次)。
281        assert_eq!(
282            CORE_CALLS.load(Ordering::SeqCst),
283            3,
284            "ScopedCore 工厂应执行 3 次(三 scope 各一次)"
285        );
286    }
287}