Skip to main content

sz_rust_core/container/
mod.rs

1//! App 容器 — DB/Cache/Log 单例 + DI 服务容器
2//!
3//! 对齐 PHP `app()` 容器,持有全局配置、单例和服务绑定。
4//!
5//! ## 设计
6//!
7//! - 基于 `OnceCell` 实现全局单例(线程安全,初始化一次后只读)
8//! - 持有 `AppConfig` + 5 个 DB 连接配置 + Cache/Log 占位
9//! - 后续阶段:接入 SZ-ORM `Pool`,替换 `DatabaseConnection` 为真正的连接池
10//! - 接入 Cache facade
11//! - 接入日志系统
12//! - DI阶段:接入服务容器(`bind`/`singleton`/`make`,对齐 PHP `app()->bind/make/singleton`)
13//!
14//! ## PHP 对齐
15//!
16//! ```php
17//! // PHP 中的 app() 容器
18//! $app = app();
19//! $db = $app->db;  // 数据库连接
20//! $cache = $app->cache;  // 缓存
21//! $log = $app->log;  // 日志
22//!
23//! // 服务绑定与解析(DI)
24//! app()->bind('cache', fn() => new MemoryCache());
25//! app()->singleton('db', fn() => Db::connect());
26//! $cache = app()->make('cache');
27//! ```
28//!
29//! ## Rust DI 设计
30//!
31//! Rust 中用 `TypeId` 替代 PHP 字符串 key,实现**类型安全**的服务解析:
32//!
33//! ```rust,ignore
34//! use sz_rust_core::container::App;
35//!
36//! // 注册单例(整个应用生命周期内只创建一次)
37//! App::with(|app| {
38//!     app.singleton(|| MyService::new());
39//! });
40//!
41//! // 解析服务(类型安全,无需 downcast 字符串 key)
42//! let svc = App::global().unwrap().make::<MyService>();
43//! ```
44
45use crate::config::{AppConfig, DatabaseConnection};
46use parking_lot::RwLock;
47use std::any::{Any, TypeId};
48use std::collections::HashMap;
49use std::sync::{Arc, OnceLock};
50
51/// 服务实例类型别名(消除 `clippy::type_complexity` 警告)
52type ServiceInstance = Arc<dyn Any + Send + Sync>;
53/// 作用域实例缓存类型别名(消除 `clippy::type_complexity` 警告)
54type ScopeInstances = HashMap<TypeId, ServiceInstance>;
55
56/// 全局 App 容器单例
57static APP: OnceLock<App> = OnceLock::new();
58
59// ============================================================================
60// DI 服务容器(对齐 PHP app()->bind/make/singleton)
61// ============================================================================
62
63/// 服务生命周期
64///
65/// 对齐 PHP `app()->bind()`(瞬态)、`app()->singleton()`(单例)、
66/// `app()->scoped()`(请求作用域)三种语义。
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum Lifetime {
69    /// 单例:整个应用生命周期内只创建一次,后续 `make` 返回同一实例
70    Singleton,
71    /// 瞬态:每次 `make` 都调用工厂创建新实例
72    Transient,
73    /// 请求作用域:同一 `ScopeId` 内单例,不同 `ScopeId` 各自独立实例
74    ///
75    /// 对齐 PHP `app()->scoped()`。在 Rust 中,`ScopeId` 通常由 Web 框架
76    /// 在请求开始时生成(如 axum 中间件生成 UUID 的低 64 位),请求结束时
77    /// 调用 [`Container::clear_scope`] 清理。
78    Scoped,
79}
80
81/// 请求作用域 ID
82///
83/// 用于 [`Container::make_with_scope`] 区分不同请求的作用域实例。
84/// 同一 `ScopeId` 内的 `make_with_scope` 调用返回同一实例。
85///
86/// P3 解环:定义迁移至 sz-rust-middleware-facade(与 `request_scope` 状态同处一地),
87/// 此处 re-export 保留 `sz_rust_core::container::ScopeId` 向后兼容路径。
88pub use sz_rust_middleware_facade::ScopeId;
89
90/// 服务工厂函数类型
91///
92/// 返回 `Box<dyn Any + Send + Sync>` 以支持任意类型的服务实例。
93type ServiceFactory = Arc<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>;
94
95/// 服务绑定(工厂 + 生命周期)
96///
97/// `Clone` 用于在 `make` 中将绑定从读锁作用域复制出来后再调用工厂,
98/// 避免在持锁状态下调用用户代码(可能引发死锁或重入)。
99#[derive(Clone)]
100struct ServiceBinding {
101    /// 工厂函数(创建服务实例)
102    factory: ServiceFactory,
103    /// 生命周期策略
104    lifetime: Lifetime,
105}
106
107/// 上下文绑定工厂:无参闭包返回任意值(对齐 PHP `give()` 的工厂)
108type ContextBindingFactory = Arc<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>;
109
110/// 上下文绑定表:key = (消费者 TypeId, 需求 TypeId),value = 工厂
111type ContextBindingMap = HashMap<(TypeId, TypeId), ContextBindingFactory>;
112
113/// DI 服务容器 — 服务注册/解析/生命周期管理
114///
115/// 对齐 PHP `app()->bind()/make()/singleton()/instance()/scoped()/alias()`。
116/// 使用 `TypeId` 作为 key 实现类型安全的服务解析,避免 PHP 字符串 key
117/// 的类型不匹配风险。
118///
119/// ## 线程安全
120///
121/// - `bindings`、`instances`、`scoped_instances`、`aliases` 均使用 `RwLock` 保护
122/// - 单例实例以 `Arc` 返回,可跨线程共享
123pub struct Container {
124    /// 服务绑定表(TypeId → 工厂 + 生命周期)
125    bindings: RwLock<HashMap<TypeId, ServiceBinding>>,
126    /// 单例实例缓存(TypeId → 已创建实例)
127    instances: RwLock<HashMap<TypeId, ServiceInstance>>,
128    /// 请求作用域实例缓存(ScopeId → (TypeId → 实例))
129    ///
130    /// 对齐 PHP `app()->scoped()`。每个 ScopeId 相当于一个"请求作用域",
131    /// 同一作用域内首次 `make_with_scope` 调用工厂创建并缓存,后续直接返回缓存。
132    scoped_instances: RwLock<HashMap<ScopeId, ScopeInstances>>,
133    /// 字符串别名表(alias → TypeId)
134    ///
135    /// 对齐 PHP `app()->alias('name', Service::class)`。
136    /// 仅用于调试输出和 `resolve_alias` 反向查找;解析时仍用类型安全的 `make::<T>()`。
137    aliases: RwLock<HashMap<String, TypeId>>,
138    /// 标签绑定表(tag → `Vec<TypeId\>`)
139    ///
140    /// 对齐 PHP `app()->tag(['Logger', 'Mailer'], 'reporters')`。
141    /// 通过 `tagged::<T>()` 获取标签下所有类型匹配的实例。
142    tags: RwLock<HashMap<String, Vec<TypeId>>>,
143    /// 上下文绑定表((消费者 TypeId, 需求 TypeId) → 工厂)
144    ///
145    /// 对齐 PHP `app()->when(PhotoController::class)->needs(Filesystem::class)->give(S3Filesystem::class)`。
146    /// 通过 `make_for::<T, Consumer>()` 为指定消费者解析上下文绑定的服务。
147    context_bindings: RwLock<ContextBindingMap>,
148    /// 循环依赖检测栈:记录当前正在构造中的服务类型链
149    ///
150    /// 用于检测 A → B → C → A 形式的循环依赖。
151    /// 工厂调用期间若发现目标类型已在栈中,立即 panic 并输出完整依赖链。
152    ///
153    /// 存储 `(&'static str, TypeId)` 对:TypeId 用于 O(1) 查找,
154    /// 类型名用于生成可读的错误信息(如 "ServiceA -> ServiceB -> ServiceA")。
155    constructing: RwLock<Vec<(&'static str, TypeId)>>,
156}
157
158impl Container {
159    /// 创建空的服务容器
160    pub fn new() -> Self {
161        Self {
162            bindings: RwLock::new(HashMap::new()),
163            instances: RwLock::new(HashMap::new()),
164            scoped_instances: RwLock::new(HashMap::new()),
165            aliases: RwLock::new(HashMap::new()),
166            tags: RwLock::new(HashMap::new()),
167            context_bindings: RwLock::new(HashMap::new()),
168            constructing: RwLock::new(Vec::new()),
169        }
170    }
171
172    /// 注册瞬态服务(每次 `make` 创建新实例)
173    ///
174    /// 对齐 PHP `app()->bind('key', fn() => new Service())`。
175    ///
176    /// # 类型约束
177    ///
178    /// - `T: Send + Sync + 'static`:服务实例必须线程安全
179    /// - `F: Fn() -> T + Send + Sync + 'static`:工厂必须线程安全
180    pub fn bind<T, F>(&self, factory: F)
181    where
182        T: Send + Sync + 'static,
183        F: Fn() -> T + Send + Sync + 'static,
184    {
185        let type_id = TypeId::of::<T>();
186        let binding = ServiceBinding {
187            factory: Arc::new(move || Box::new(factory())),
188            lifetime: Lifetime::Transient,
189        };
190        self.bindings.write().insert(type_id, binding);
191    }
192
193    /// 注册单例服务(整个应用生命周期内只创建一次)
194    ///
195    /// 对齐 PHP `app()->singleton('key', fn() => new Service())`。
196    ///
197    /// 首次 `make` 时调用工厂创建实例并缓存,后续 `make` 返回缓存的同一实例。
198    pub fn singleton<T, F>(&self, factory: F)
199    where
200        T: Send + Sync + 'static,
201        F: Fn() -> T + Send + Sync + 'static,
202    {
203        let type_id = TypeId::of::<T>();
204        let binding = ServiceBinding {
205            factory: Arc::new(move || Box::new(factory())),
206            lifetime: Lifetime::Singleton,
207        };
208        self.bindings.write().insert(type_id, binding);
209    }
210
211    /// 注册请求作用域服务(同一 `ScopeId` 内单例)
212    ///
213    /// 对齐 PHP `app()->scoped('key', fn() => new Service())`。
214    ///
215    /// 与 `singleton` 不同:scoped 服务在 [`Container::make_with_scope`] 调用时,
216    /// 同一 `scope_id` 内首次调用工厂创建并缓存,后续返回缓存;
217    /// 不同 `scope_id` 各自创建独立实例;请求结束后调用
218    /// [`Container::clear_scope`] 清理对应作用域的缓存。
219    ///
220    /// # 用法
221    ///
222    /// ```ignore
223    /// use sz_rust_core::container::{Container, ScopeId};
224    ///
225    /// let container = Container::new();
226    /// container.scoped(|| RequestCache::new());
227    ///
228    /// // 请求 A(scope_id=1)
229    /// let cache_a1 = container.make_with_scope::<RequestCache>(1).unwrap();
230    /// let cache_a2 = container.make_with_scope::<RequestCache>(1).unwrap();
231    /// assert!(Arc::ptr_eq(&cache_a1, &cache_a2)); // 同一作用域:同一实例
232    ///
233    /// // 请求 B(scope_id=2)
234    /// let cache_b = container.make_with_scope::<RequestCache>(2).unwrap();
235    /// assert!(!Arc::ptr_eq(&cache_a1, &cache_b)); // 不同作用域:不同实例
236    ///
237    /// // 请求 A 结束
238    /// container.clear_scope(1);
239    /// ```
240    pub fn scoped<T, F>(&self, factory: F)
241    where
242        T: Send + Sync + 'static,
243        F: Fn() -> T + Send + Sync + 'static,
244    {
245        let type_id = TypeId::of::<T>();
246        let binding = ServiceBinding {
247            factory: Arc::new(move || Box::new(factory())),
248            lifetime: Lifetime::Scoped,
249        };
250        self.bindings.write().insert(type_id, binding);
251    }
252
253    /// 直接绑定已创建的实例(绕过工厂)
254    ///
255    /// 对齐 PHP `app()->instance('key', $obj)`。
256    ///
257    /// 将一个已创建的实例直接注册为单例,后续 `make` 返回此实例。
258    /// 适用于:
259    /// - 实例已在其他地方创建(如配置加载时初始化的服务)
260    /// - 实例创建过程复杂、不适合用闭包表达
261    /// - 测试中注入 mock 实例
262    ///
263    /// # 用法
264    ///
265    /// ```ignore
266    /// use sz_rust_core::container::Container;
267    ///
268    /// let container = Container::new();
269    /// let logger = Arc::new(FileLogger::new("/var/log/app.log"));
270    /// container.instance(logger.clone());
271    ///
272    /// let resolved = container.make::<FileLogger>().unwrap();
273    /// assert!(Arc::ptr_eq(&logger, &resolved));
274    /// ```
275    pub fn instance<T>(&self, instance: T)
276    where
277        T: Send + Sync + 'static,
278    {
279        let type_id = TypeId::of::<T>();
280        let arc: Arc<dyn Any + Send + Sync> = Arc::new(instance);
281        // 1. 缓存实例(make 会优先检查 instances 缓存)
282        self.instances.write().insert(type_id, arc);
283        // 2. 注册占位绑定(使 has() 返回 true)
284        // 注:factory 不会被调用,因为 make 会先命中 instances 缓存。
285        // 使用 unreachable 闭包表达此不变量;若被调用则说明内部状态被破坏。
286        self.bindings.write().insert(
287            type_id,
288            ServiceBinding {
289                factory: Arc::new(|| {
290                    panic!("instance() 绑定的服务不应调用工厂 — 这是内部不变量违反")
291                }),
292                lifetime: Lifetime::Singleton,
293            },
294        );
295    }
296
297    /// 为服务类型注册字符串别名
298    ///
299    /// 对齐 PHP `app()->alias('name', Service::class)`。
300    ///
301    /// 别名仅用于:
302    /// - 调试输出([`Container::debug_aliases`] 列出所有别名)
303    /// - 反向查找([`Container::resolve_alias`] 通过别名获取 TypeId)
304    ///
305    /// 解析时仍用类型安全的 `make::<T>()`,不支持通过字符串别名解析
306    /// (Rust 类型系统要求编译时已知类型,字符串 key 解析会引入不安全的 downcast)。
307    ///
308    /// # 用法
309    ///
310    /// ```ignore
311    /// use sz_rust_core::container::Container;
312    ///
313    /// let container = Container::new();
314    /// container.singleton(|| MyService::new());
315    /// container.alias::<MyService>("my_service");
316    ///
317    /// assert!(container.is_alias("my_service"));
318    /// let type_id = container.resolve_alias("my_service").unwrap();
319    /// assert_eq!(type_id, std::any::TypeId::of::<MyService>());
320    /// ```
321    pub fn alias<T: 'static>(&self, name: impl Into<String>) {
322        let type_id = TypeId::of::<T>();
323        self.aliases.write().insert(name.into(), type_id);
324    }
325
326    /// 通过别名查找对应的 TypeId
327    ///
328    /// 返回 `None` 表示别名未注册。
329    pub fn resolve_alias(&self, name: &str) -> Option<TypeId> {
330        self.aliases.read().get(name).copied()
331    }
332
333    /// 检查指定别名是否已注册
334    pub fn is_alias(&self, name: &str) -> bool {
335        self.aliases.read().contains_key(name)
336    }
337
338    /// 列出所有已注册别名(用于调试)
339    pub fn debug_aliases(&self) -> Vec<String> {
340        self.aliases.read().keys().cloned().collect()
341    }
342
343    /// 解析服务实例(无作用域)
344    ///
345    /// 对齐 PHP `app()->make('key')`。
346    ///
347    /// 等价于 [`Container::make_with_scope`] 传入 `scope_id = 0`。
348    /// 对于 `Scoped` 生命周期服务,会使用 `scope_id = 0` 作为默认作用域。
349    ///
350    /// # 返回
351    ///
352    /// - `Some(Arc<T>)`:服务已注册,返回实例(单例返回缓存实例,瞬态返回新实例)
353    /// - `None`:服务未注册
354    ///
355    /// # Panics
356    ///
357    /// 理论上不会 panic(工厂返回的 `Box<dyn Any>` 内部类型由编译时泛型保证)。
358    /// 若发生 panic 说明内部状态被破坏(bindings 与 instances 不一致)。
359    #[inline]
360    pub fn make<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
361        self.make_with_scope::<T>(0)
362    }
363
364    /// 解析服务实例(带作用域 ID)
365    ///
366    /// 对齐 PHP `app()->make('key')` + 请求作用域支持。
367    ///
368    /// # 生命周期处理
369    ///
370    /// - `Singleton`:忽略 `scope_id`,返回全局缓存的单例
371    /// - `Transient`:忽略 `scope_id`,每次调用工厂创建新实例
372    /// - `Scoped`:同一 `scope_id` 内首次调用工厂创建并缓存,后续返回缓存
373    ///
374    /// # 返回
375    ///
376    /// - `Some(Arc<T>)`:服务已注册,返回实例
377    /// - `None`:服务未注册
378    ///
379    /// # Panics
380    ///
381    /// 理论上不会 panic(工厂返回的 `Box<dyn Any>` 内部类型由编译时泛型保证)。
382    /// 若发生 panic 说明内部状态被破坏。
383    pub fn make_with_scope<T: Send + Sync + 'static>(&self, scope_id: ScopeId) -> Option<Arc<T>> {
384        let type_id = TypeId::of::<T>();
385
386        // 1. 检查全局单例缓存(singleton 和 instance 都会写入此缓存)
387        if let Some(cached) = self.instances.read().get(&type_id) {
388            return Arc::downcast::<T>(cached.clone()).ok();
389        }
390
391        // 2. 检查作用域缓存(仅 Scoped 生命周期)
392        if scope_id != 0 {
393            let scoped = self.scoped_instances.read();
394            if let Some(scope_map) = scoped.get(&scope_id) {
395                if let Some(cached) = scope_map.get(&type_id) {
396                    return Arc::downcast::<T>(cached.clone()).ok();
397                }
398            }
399        }
400
401        // 3. 查找绑定
402        // 注:先绑定 `let` 延长 `RwLockReadGuard` 生命周期,避免临时值被释放
403        let guard = self.bindings.read();
404        let binding = guard.get(&type_id)?.clone();
405        drop(guard); // 释放读锁后再调用工厂(避免持锁调用用户代码引发死锁/重入)
406
407        // 4. 循环依赖检测 + 工厂调用(共用逻辑,含 make_for)
408        let type_name = std::any::type_name::<T>();
409        let instance = self.check_and_call_factory(type_id, type_name, &binding.factory)?;
410
411        match binding.lifetime {
412            Lifetime::Singleton => {
413                let arc: Arc<dyn Any + Send + Sync> = Arc::from(instance);
414                self.instances.write().insert(type_id, arc.clone());
415                Arc::downcast::<T>(arc).ok()
416            }
417            Lifetime::Scoped => {
418                let arc: Arc<dyn Any + Send + Sync> = Arc::from(instance);
419                self.scoped_instances
420                    .write()
421                    .entry(scope_id)
422                    .or_default()
423                    .insert(type_id, arc.clone());
424                Arc::downcast::<T>(arc).ok()
425            }
426            Lifetime::Transient => {
427                // 瞬态:直接返回(不缓存)
428                Arc::downcast::<T>(Arc::from(instance)).ok()
429            }
430        }
431    }
432
433    /// 清理指定作用域的所有缓存实例
434    ///
435    /// 应在请求结束时调用(如 axum 中间件在请求处理完毕后调用),
436    /// 释放该作用域内创建的所有 Scoped 服务实例。
437    ///
438    /// # 用法
439    ///
440    /// ```ignore
441    /// use sz_rust_core::container::Container;
442    ///
443    /// let container = Container::new();
444    /// container.scoped(|| RequestCache::new());
445    ///
446    /// let scope_id = generate_scope_id(); // 如从 axum State 获取
447    /// let _cache = container.make_with_scope::<RequestCache>(scope_id);
448    ///
449    /// // 请求结束
450    /// container.clear_scope(scope_id);
451    /// ```
452    pub fn clear_scope(&self, scope_id: ScopeId) {
453        self.scoped_instances.write().remove(&scope_id);
454    }
455
456    /// 检查服务是否已注册
457    pub fn has<T: 'static>(&self) -> bool {
458        let type_id = TypeId::of::<T>();
459        self.bindings.read().contains_key(&type_id)
460    }
461
462    /// 移除指定类型的服务绑定(含单例缓存与所有作用域缓存)
463    ///
464    /// 对齐 PHP `app()->remove('key')`。
465    pub fn forget<T: 'static>(&self) {
466        let type_id = TypeId::of::<T>();
467        self.bindings.write().remove(&type_id);
468        self.instances.write().remove(&type_id);
469        // 清理所有作用域中该类型的缓存
470        let mut scoped = self.scoped_instances.write();
471        for scope_map in scoped.values_mut() {
472            scope_map.remove(&type_id);
473        }
474    }
475
476    /// 清空所有服务绑定与缓存(含单例、作用域、别名、标签、上下文绑定)
477    pub fn clear(&self) {
478        self.bindings.write().clear();
479        self.instances.write().clear();
480        self.scoped_instances.write().clear();
481        self.aliases.write().clear();
482        self.tags.write().clear();
483        self.context_bindings.write().clear();
484        self.constructing.write().clear();
485    }
486
487    /// 当前构造栈深度(用于调试,正常应为 0)
488    pub fn constructing_depth(&self) -> usize {
489        self.constructing.read().len()
490    }
491
492    /// 已注册服务数量(不含别名)
493    pub fn count(&self) -> usize {
494        self.bindings.read().len()
495    }
496
497    /// 已注册别名数量
498    pub fn alias_count(&self) -> usize {
499        self.aliases.read().len()
500    }
501
502    /// 当前活跃作用域数量
503    ///
504    /// 可用于检测作用域泄漏(如请求结束未调用 `clear_scope`)。
505    pub fn active_scope_count(&self) -> usize {
506        self.scoped_instances.read().len()
507    }
508
509    // ========================================================================
510    // 标签绑定(对齐 PHP `app()->tag()` / `app()->tagged()`)
511    // ========================================================================
512
513    /// 给类型 T 打标签(对齐 PHP `app()->tag(['Service'], 'tag_name')`)
514    ///
515    /// PHP 用法:
516    /// ```php
517    /// $this->app->tag(['Logger', 'Mailer', 'Notifier'], 'reporters');
518    /// ```
519    ///
520    /// Rust 端由于类型安全,每次调用只能给一个类型打标签。
521    /// 多次调用同一标签名会追加到标签列表。
522    ///
523    /// # 用法
524    ///
525    /// ```ignore
526    /// use sz_rust_core::container::Container;
527    ///
528    /// let container = Container::new();
529    /// container.singleton(|| FileLogger::new());
530    /// container.singleton(|| MailLogger::new());
531    ///
532    /// container.tag::<FileLogger>("reporters");
533    /// container.tag::<MailLogger>("reporters");
534    ///
535    /// let reporters = container.tagged::<FileLogger>("reporters");
536    /// assert_eq!(reporters.len(), 1);
537    /// ```
538    pub fn tag<T: 'static>(&self, tag: impl Into<String>) {
539        let type_id = TypeId::of::<T>();
540        let tag_name = tag.into();
541        self.tags.write().entry(tag_name).or_default().push(type_id);
542    }
543
544    /// 获取标签下所有 T 类型实例(对齐 PHP `app()->tagged('tag_name')`)
545    ///
546    /// 遍历标签下所有 TypeId,对每个匹配 `T` 的 TypeId 调用 `make::<T>()`。
547    ///
548    /// # 返回
549    ///
550    /// 标签下所有类型为 `T` 的服务实例向量。若标签不存在或无匹配类型,返回空向量。
551    pub fn tagged<T: Send + Sync + 'static>(&self, tag: &str) -> Vec<Arc<T>> {
552        let type_ids = match self.tags.read().get(tag) {
553            Some(ids) => ids.clone(),
554            None => return Vec::new(),
555        };
556
557        let target_type_id = TypeId::of::<T>();
558        type_ids
559            .into_iter()
560            .filter(|id| *id == target_type_id)
561            .filter_map(|_| self.make::<T>())
562            .collect()
563    }
564
565    /// 获取标签下所有 TypeId(用于调试)
566    ///
567    /// 返回标签下所有已注册的 TypeId 列表。若标签不存在,返回空向量。
568    pub fn tagged_type_ids(&self, tag: &str) -> Vec<TypeId> {
569        self.tags.read().get(tag).cloned().unwrap_or_default()
570    }
571
572    /// 获取已注册标签列表
573    pub fn tag_names(&self) -> Vec<String> {
574        self.tags.read().keys().cloned().collect()
575    }
576
577    /// 获取标签下已注册的类型数量
578    pub fn tag_count(&self, tag: &str) -> usize {
579        self.tags.read().get(tag).map(|ids| ids.len()).unwrap_or(0)
580    }
581
582    /// 移除指定标签(对齐 PHP `app()->forgetTag('tag_name')`)
583    pub fn forget_tag(&self, tag: &str) {
584        self.tags.write().remove(tag);
585    }
586
587    // ========================================================================
588    // 上下文绑定(对齐 PHP `app()->when()->needs()->give()`)
589    // ========================================================================
590
591    /// 注册上下文绑定(对齐 PHP `app()->when(Consumer)->needs(Need)->give(impl)`)
592    ///
593    /// PHP 用法:
594    /// ```php
595    /// $this->app->when(PhotoController::class)
596    ///     ->needs(Filesystem::class)
597    ///     ->give(function () { return new S3Filesystem(); });
598    /// ```
599    ///
600    /// Rust 端通过泛型参数指定消费者类型 `Consumer`、需求类型 `T`,
601    /// 并提供工厂闭包创建 `T` 实例。
602    ///
603    /// # 用法
604    ///
605    /// ```ignore
606    /// use sz_rust_core::container::Container;
607    ///
608    /// let container = Container::new();
609    ///
610    /// // 为 PhotoController 注入 S3Filesystem 作为 Filesystem
611    /// container.bind_contextual::<PhotoController, Filesystem, _>(|| {
612    ///     S3Filesystem::new()
613    /// });
614    ///
615    /// // 解析:为 PhotoController 创建 Filesystem 实例
616    /// let fs = container.make_for::<Filesystem, PhotoController>();
617    /// ```
618    ///
619    /// # 注意
620    ///
621    /// 上下文绑定不会缓存实例(每次 `make_for` 调用工厂)。
622    pub fn bind_contextual<Consumer: 'static, T: Send + Sync + 'static, F>(&self, factory: F)
623    where
624        F: Fn() -> T + Send + Sync + 'static,
625    {
626        let key = (TypeId::of::<Consumer>(), TypeId::of::<T>());
627        let arc_factory: Arc<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync> =
628            Arc::new(move || Box::new(factory()));
629        self.context_bindings.write().insert(key, arc_factory);
630    }
631
632    /// 为指定消费者解析上下文绑定的服务(对齐 PHP 上下文感知 `make`)
633    ///
634    /// 查找 `(Consumer, T)` 的上下文绑定,若存在则调用工厂返回实例。
635    /// 若不存在上下文绑定,回退到普通 `make::<T>()`。
636    ///
637    /// # 返回
638    ///
639    /// - `Some(Arc<T>)`:找到上下文绑定或普通绑定
640    /// - `None`:既无上下文绑定也无普通绑定
641    pub fn make_for<T: Send + Sync + 'static, Consumer: 'static>(&self) -> Option<Arc<T>> {
642        let key = (TypeId::of::<Consumer>(), TypeId::of::<T>());
643
644        // 1. 检查上下文绑定
645        let factory = {
646            let guard = self.context_bindings.read();
647            guard.get(&key).cloned()
648        };
649
650        if let Some(factory) = factory {
651            // 上下文绑定同样需要循环依赖检测
652            let type_id = TypeId::of::<T>();
653            let type_name = std::any::type_name::<T>();
654            let instance = self.check_and_call_factory(type_id, type_name, &factory);
655            instance.and_then(|inst| Arc::downcast::<T>(Arc::from(inst)).ok())
656        } else {
657            // 2. 回退到普通 make
658            self.make::<T>()
659        }
660    }
661
662    /// 循环依赖检测 + 工厂调用(供 make_with_scope 和 make_for 共用)
663    ///
664    /// 检查目标类型是否已在构造栈中,若是则 panic;
665    /// 否则压栈、调用工厂、弹栈,返回原始工厂输出(`Box<dyn Any + Send + Sync>`)。
666    /// 调用方负责将 `Box` 转为 `Arc` 并按生命周期策略缓存。
667    fn check_and_call_factory(
668        &self,
669        type_id: TypeId,
670        type_name: &'static str,
671        factory: &ServiceFactory,
672    ) -> Option<Box<dyn Any + Send + Sync>> {
673        // 循环依赖检测
674        {
675            let constructing = self.constructing.read();
676            if constructing.iter().any(|(_, tid)| *tid == type_id) {
677                let chain: Vec<&str> = constructing
678                    .iter()
679                    .skip_while(|(_, tid)| *tid != type_id)
680                    .map(|(name, _)| *name)
681                    .chain(std::iter::once(type_name))
682                    .collect();
683                drop(constructing);
684                panic!("DI 容器检测到循环依赖: {}", chain.join(" -> "));
685            }
686        }
687
688        self.constructing.write().push((type_name, type_id));
689        let instance = factory();
690        self.constructing.write().pop();
691
692        Some(instance)
693    }
694
695    /// 检查指定上下文绑定是否存在
696    pub fn has_contextual<Consumer: 'static, T: 'static>(&self) -> bool {
697        let key = (TypeId::of::<Consumer>(), TypeId::of::<T>());
698        self.context_bindings.read().contains_key(&key)
699    }
700
701    /// 获取上下文绑定数量
702    pub fn contextual_count(&self) -> usize {
703        self.context_bindings.read().len()
704    }
705
706    /// 移除指定上下文绑定
707    pub fn forget_contextual<Consumer: 'static, T: 'static>(&self) {
708        let key = (TypeId::of::<Consumer>(), TypeId::of::<T>());
709        self.context_bindings.write().remove(&key);
710    }
711
712    // ========================================================================
713    // 方法调用 / 自动注入(对齐 PHP `app()->call()` / `app()->invoke()`)
714    // ========================================================================
715
716    /// 调用闭包并自动注入参数 — 对齐 PHP `app()->call($callback, $parameters)`
717    ///
718    /// PHP 的 `app()->call()` 通过反射自动解析方法参数类型并从容器获取实例。
719    /// Rust 是静态类型语言,无法运行时反射,因此通过 `resolver` 闭包手工指定
720    /// 如何从 Container 解析参数。
721    ///
722    /// # 参数
723    ///
724    /// - `resolver`: 参数解析器,接收 `&Container` 引用,返回参数元组
725    /// - `callback`: 业务回调,接收解析后的参数,返回业务结果
726    ///
727    /// # 返回
728    ///
729    /// 业务回调的返回值
730    ///
731    /// # 用法
732    ///
733    /// ```ignore
734    /// use sz_rust_core::container::Container;
735    ///
736    /// struct UserService;
737    /// struct Logger;
738    ///
739    /// let container = Container::new();
740    /// container.singleton(Logger::new);
741    /// container.singleton(UserService::new);
742    ///
743    /// // 自动注入 Logger 和 UserService
744    /// let result: String = container.call_method(
745    ///     |c| (c.make::<Logger>().unwrap(), c.make::<UserService>().unwrap()),
746    ///     |(logger, service)| {
747    ///         format!("called with logger and service")
748    ///     },
749    /// );
750    /// ```
751    pub fn call_method<R, P, F, C>(&self, resolver: C, callback: F) -> R
752    where
753        F: FnOnce(P) -> R,
754        C: FnOnce(&Self) -> P,
755    {
756        let params = resolver(self);
757        callback(params)
758    }
759
760    /// 调用闭包并传入容器引用 — 对齐 PHP `app()->invoke($callback)`
761    ///
762    /// 最灵活的方法调用方式,调用方可以在闭包内自由调用 `make()` 解析依赖。
763    ///
764    /// # 用法
765    ///
766    /// ```ignore
767    /// let result: String = container.invoke(|c| {
768    ///     let logger = c.make::<Logger>().unwrap();
769    ///     let service = c.make::<UserService>().unwrap();
770    ///     format!("called with {:?} and {:?}", logger, service)
771    /// });
772    /// ```
773    pub fn invoke<R, F>(&self, callback: F) -> R
774    where
775        F: FnOnce(&Self) -> R,
776    {
777        callback(self)
778    }
779
780    /// 解析服务,失败时 panic — 用于自动注入场景
781    ///
782    /// 对齐 PHP `app()->make()` 在服务未注册时抛出异常的行为。
783    /// Rust 端通过 panic 模拟,调用方应在确保服务已注册时使用。
784    ///
785    /// # Panics
786    ///
787    /// 当服务未注册时 panic。
788    #[inline]
789    pub fn make_or_panic<T: Send + Sync + 'static>(&self) -> Arc<T> {
790        match self.make::<T>() {
791            Some(instance) => instance,
792            None => panic!(
793                "无法解析服务: {} — 请确保已通过 bind/singleton/scoped 注册",
794                std::any::type_name::<T>()
795            ),
796        }
797    }
798}
799
800impl Default for Container {
801    fn default() -> Self {
802        Self::new()
803    }
804}
805
806impl std::fmt::Debug for Container {
807    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
808        f.debug_struct("Container")
809            .field("bindings_count", &self.bindings.read().len())
810            .field("instances_count", &self.instances.read().len())
811            .field("scoped_scope_count", &self.scoped_instances.read().len())
812            .field("aliases_count", &self.aliases.read().len())
813            .finish()
814    }
815}
816
817// ============================================================================
818// App 容器(全局单例)
819// ============================================================================
820
821/// App 容器(全局单例)
822///
823/// 持有应用配置、各子系统单例和 DI 服务容器。通过 [`App::global()`] 获取全局实例,
824/// 通过 [`App::init()`] 初始化。
825pub struct App {
826    /// 应用配置(只读,初始化后不可变)
827    config: AppConfig,
828    /// 数据库连接配置(5 个:mysql/njszjt/ljclz/food/oceanbase)
829    /// 后续将替换为 SZ-ORM `Pool` 实例
830    db_connections: HashMap<String, DatabaseConnection>,
831    /// Cache 单例占位(接入真正的 Cache facade)
832    cache: RwLock<Option<String>>,
833    /// Log 单例占位(接入 sz-orm-logger + tracing)
834    log: RwLock<Option<String>>,
835    /// DI 服务容器(服务注册/解析/生命周期管理)
836    container: Container,
837}
838
839impl App {
840    /// 构造 App 实例(不注册到全局单例)
841    ///
842    /// 用于测试或显式持有实例的场景。生产代码应使用 [`App::init()`] 注册全局单例。
843    pub fn new(config: AppConfig) -> App {
844        let db_connections = config.database.connections.clone();
845        App {
846            config,
847            db_connections,
848            cache: RwLock::new(None),
849            log: RwLock::new(None),
850            container: Container::new(),
851        }
852    }
853
854    /// 初始化全局 App 容器
855    ///
856    /// 只能调用一次,重复调用返回已有实例。
857    ///
858    /// ```rust,ignore
859    /// use sz_rust_core::container::App;
860    /// use sz_rust_core::config::AppConfig;
861    ///
862    /// let config = AppConfig::load_from_dir("config").await.unwrap();
863    /// let app = App::init(config);
864    /// ```
865    pub fn init(config: AppConfig) -> &'static App {
866        APP.get_or_init(|| App::new(config))
867    }
868
869    /// 获取全局 App 容器实例
870    ///
871    /// 必须先调用 [`App::init()`] 初始化,否则返回 `None`。
872    ///
873    /// # 命名说明
874    ///
875    /// 此方法对应 PHP `app()` helper(获取全局容器实例)。
876    /// 不使用 `App::instance()` 是为了避免与 [`App::instance<T>`](绑定实例方法,
877    /// 对齐 PHP `app()->instance('key', $obj)`)冲突。
878    pub fn global() -> Option<&'static App> {
879        APP.get()
880    }
881
882    /// 获取应用配置
883    pub fn config(&self) -> &AppConfig {
884        &self.config
885    }
886
887    /// 获取数据库连接配置
888    ///
889    /// 对齐 PHP `Db::connect('mysql')`。
890    ///
891    /// 当前返回 `DatabaseConnection` 配置。
892    /// 后续将替换为 SZ-ORM `Pool` 实例。
893    pub fn db_connection(&self, name: &str) -> Option<&DatabaseConnection> {
894        self.db_connections.get(name)
895    }
896
897    /// 获取所有数据库连接名称
898    pub fn db_connection_names(&self) -> Vec<&str> {
899        self.db_connections.keys().map(|s| s.as_str()).collect()
900    }
901
902    /// 获取默认数据库连接配置
903    pub fn default_db_connection(&self) -> Option<&DatabaseConnection> {
904        self.db_connection(&self.config.database.default)
905    }
906
907    /// 设置 Cache 单例(将替换为真正的 Cache facade)
908    pub fn set_cache(&self, cache: impl Into<String>) {
909        let mut guard = self.cache.write();
910        *guard = Some(cache.into());
911    }
912
913    /// 获取 Cache 单例
914    pub fn cache(&self) -> Option<String> {
915        self.cache.read().clone()
916    }
917
918    /// 设置 Log 单例(将替换为真正的日志系统)
919    pub fn set_log(&self, log: impl Into<String>) {
920        let mut guard = self.log.write();
921        *guard = Some(log.into());
922    }
923
924    /// 获取 Log 单例
925    pub fn log(&self) -> Option<String> {
926        self.log.read().clone()
927    }
928
929    // ========================================================================
930    // DI 服务容器代理方法(对齐 PHP app()->bind/make/singleton/scoped/instance/alias)
931    // ========================================================================
932
933    /// 获取 DI 服务容器引用
934    pub fn container(&self) -> &Container {
935        &self.container
936    }
937
938    /// 注册瞬态服务
939    ///
940    /// 对齐 PHP `app()->bind('key', fn() => new Service())`。
941    pub fn bind<T, F>(&self, factory: F)
942    where
943        T: Send + Sync + 'static,
944        F: Fn() -> T + Send + Sync + 'static,
945    {
946        self.container.bind(factory);
947    }
948
949    /// 注册单例服务
950    ///
951    /// 对齐 PHP `app()->singleton('key', fn() => new Service())`。
952    pub fn singleton<T, F>(&self, factory: F)
953    where
954        T: Send + Sync + 'static,
955        F: Fn() -> T + Send + Sync + 'static,
956    {
957        self.container.singleton(factory);
958    }
959
960    /// 注册请求作用域服务
961    ///
962    /// 对齐 PHP `app()->scoped('key', fn() => new Service())`。
963    pub fn scoped<T, F>(&self, factory: F)
964    where
965        T: Send + Sync + 'static,
966        F: Fn() -> T + Send + Sync + 'static,
967    {
968        self.container.scoped(factory);
969    }
970
971    /// 直接绑定已创建的实例
972    ///
973    /// 对齐 PHP `app()->instance('key', $obj)`。
974    pub fn instance<T>(&self, instance: T)
975    where
976        T: Send + Sync + 'static,
977    {
978        self.container.instance(instance);
979    }
980
981    /// 为服务类型注册字符串别名
982    ///
983    /// 对齐 PHP `app()->alias('name', Service::class)`。
984    pub fn alias<T: 'static>(&self, name: impl Into<String>) {
985        self.container.alias::<T>(name);
986    }
987
988    /// 解析服务实例(自动感知请求作用域)
989    ///
990    /// 对齐 PHP `app()->make('key')`。
991    ///
992    /// P1-ARCH-DI-02:若当前线程处于 [`RequestScopeLayer`](crate::middleware::request_scope::RequestScopeLayer)
993    /// 管理的请求中(即 `current_scope_id()` 返回 `Some`),则自动路由到
994    /// `make_with_scope`,使 Scoped 绑定在请求内缓存、请求结束清理。
995    /// 请求外调用则退化为无作用域的 `make`(向后兼容)。
996    #[inline]
997    pub fn make<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
998        if let Some(scope_id) = crate::middleware::request_scope::current_scope_id() {
999            self.container.make_with_scope::<T>(scope_id)
1000        } else {
1001            self.container.make::<T>()
1002        }
1003    }
1004
1005    /// 解析服务实例(带作用域 ID)
1006    ///
1007    /// 对齐 PHP `app()->make('key')` + 请求作用域支持。
1008    #[inline]
1009    pub fn make_with_scope<T: Send + Sync + 'static>(&self, scope_id: ScopeId) -> Option<Arc<T>> {
1010        self.container.make_with_scope::<T>(scope_id)
1011    }
1012
1013    /// 清理指定作用域的所有缓存实例
1014    pub fn clear_scope(&self, scope_id: ScopeId) {
1015        self.container.clear_scope(scope_id);
1016    }
1017
1018    /// 检查服务是否已注册
1019    pub fn has_service<T: 'static>(&self) -> bool {
1020        self.container.has::<T>()
1021    }
1022}
1023
1024impl std::fmt::Debug for App {
1025    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1026        f.debug_struct("App")
1027            .field("config", &self.config)
1028            .field(
1029                "db_connections",
1030                &self.db_connections.keys().collect::<Vec<_>>(),
1031            )
1032            .field("cache", &self.cache.read().is_some())
1033            .field("log", &self.log.read().is_some())
1034            .field("container", &self.container)
1035            .finish()
1036    }
1037}
1038
1039// ============================================================================
1040// 单元测试(分离到 tests.rs,降低单文件认知负担)
1041// ============================================================================
1042
1043#[cfg(test)]
1044mod tests;