tx_di_core/di/comp/mod.rs
1pub mod comp_ref;
2pub mod config;
3
4use std::any::{Any, TypeId};
5use log::debug;
6use crate::{BoxFuture, BuildContext, Scope};
7use crate::di::common::RIE;
8
9#[linkme::distributed_slice]
10pub static COMPONENT_REGISTRY: [ComponentMeta] = [..];
11
12/// 组件元数据,存储组件的运行时信息和依赖关系。
13///
14/// 该结构体由 `#[tx_comp]` 宏自动生成并注册到 `COMPONENT_REGISTRY` 中,
15/// 用于在运行时进行依赖解析、拓扑排序和组件构建。
16///
17/// # 字段说明
18///
19/// - `type_id`: 返回组件类型 `TypeId` 的函数指针,用于唯一标识组件类型
20/// - `deps`: 组件的依赖列表,每个元素是返回依赖类型 `TypeId` 的函数指针
21/// - `name`: 组件的类型名称字符串,用于调试和错误提示
22/// - `scope`: 组件的作用域(Singleton 或 Prototype),决定实例的生命周期
23/// - `factory_fn`: 可选的工厂函数,仅用于调试诊断,运行时不使用
24pub struct ComponentMeta {
25 /// 返回组件类型 `TypeId` 的函数指针。
26 ///
27 /// 用于在运行时唯一标识组件类型,支持类型安全的依赖查找和向下转型。
28 pub type_id: fn() -> TypeId,
29
30 /// 组件的依赖列表,存储为返回 `TypeId` 的函数指针数组。
31 ///
32 /// 该数组包含所有通过 `Arc<T>` 注入的依赖项(不包括 `#[tx_cst]` 标记的字段)。
33 /// 在拓扑排序阶段用于构建依赖图,检测循环依赖。
34 pub deps: &'static [fn() -> TypeId],
35
36 /// 组件的类型名称字符串。
37 ///
38 /// 用于调试输出、错误消息和日志记录,提高可读性。
39 pub name: &'static str,
40
41 /// 组件的作用域,决定实例的生命周期管理策略。
42 ///
43 /// - `Scope::Singleton`: 全局单例,首次注入时构建并缓存
44 /// - `Scope::Prototype`: 原型模式,每次注入都创建新实例
45 pub scope: Scope,
46
47 /// 原始工厂函数,仅用于 `debug_registry` 诊断;运行时不使用。
48 ///
49 /// 该字段为可选,主要用于调试和开发阶段的组件信息查看。
50 /// 在正式运行时,组件构建通过 `ComponentDescriptor::build` 方法完成。
51 pub factory_fn: Option<fn(&mut BuildContext) -> Box<dyn Any + Send + Sync>>,
52
53 pub init_sort_fn: fn() -> i32,
54 pub init_fn: Option<fn(&mut BuildContext) -> RIE<()>>,
55 pub async_init_fn: Option<fn(&mut BuildContext)-> BoxFuture<'static, RIE<()>>>,
56}
57
58/// 对组件元数据进行拓扑排序,确定组件的构建顺序。 `Kahn算法` todo 不但拓扑排序,还要满足定义的初始化顺序排序
59///
60/// 该函数基于组件的依赖关系图执行拓扑排序,确保在构建组件时,
61/// 其所有依赖项已经被构建并可用。如果检测到循环依赖,将触发 panic。
62///
63/// # 参数
64///
65/// - `metas`: 组件元数据切片引用,包含所有需要排序的组件信息。
66/// 每个元素是指向 `ComponentMeta` 的引用,提供类型 ID、依赖列表等关键信息。
67///
68/// # 返回值
69///
70/// 返回按拓扑顺序排列的 `TypeId` 向量。向量中的类型 ID 顺序保证了:
71/// 对于任意组件,其所有依赖项都出现在该组件之前。
72///
73/// # Panics
74///
75/// 以下情况会触发 panic:
76/// - 某个组件依赖的类型未在注册表中找到
77/// - 检测到循环依赖(即存在无法解析的依赖环)
78/// - 内部错误:TypeId 在名称映射中未找到
79///
80/// # 性能
81///
82/// 使用 Kahn 算法实现拓扑排序,时间复杂度为 O(V + E),
83/// 其中 V 是组件数量,E 是依赖关系数量。
84/// 函数会记录排序结果和耗时到 debug 日志中。
85pub fn topo_sort(metas: &[&ComponentMeta]) -> Vec<TypeId> {
86 let start = std::time::Instant::now();
87
88 use std::collections::{HashMap, VecDeque};
89
90 let n = metas.len();
91
92 let id_to_idx: HashMap<TypeId, (usize,&str)> = metas
93 .iter()
94 .enumerate()
95 .map(|(i, m)| ((m.type_id)(), (i,m.name)))
96 .collect();
97 // 入度数组:记录每个组件被多少其他组件依赖
98 let mut in_degree = vec![0usize; n];
99 // 邻接表:adj[j] 存储所有依赖组件 j 的组件索引
100 let mut adj: Vec<Vec<usize>> = vec![vec![]; n];
101 // 遍历每个组件 i
102 for (i, meta) in metas.iter().enumerate() {
103 // 遍历组件 i 的所有依赖
104 for dep_fn in meta.deps {
105 // 获取依赖的类型ID
106 let one_type_id = dep_fn();
107 if let Some(&j) = id_to_idx.get(&one_type_id) {
108 adj[j.0].push(i); // 建立边:j → i(j 被 i 依赖)
109 in_degree[i] += 1;
110 } else {
111 panic!(
112 "[di] 组件 '{}' 依赖的类型 {:?} {:?} 未在注册表中找到",
113 meta.name,
114 id_to_idx.get(&one_type_id),
115 &one_type_id
116 ); // 依赖未注册,报错
117 }
118 }
119 }
120 // 将所有入度为 0 的节点加入队列(无依赖的组件)
121 let mut queue: VecDeque<usize> = (0..n).filter(|&i| in_degree[i] == 0).collect();
122 let mut result = Vec::with_capacity(n);
123
124 while let Some(i) = queue.pop_front() {
125 // 将当前组件加入结果
126 result.push((metas[i].type_id)());
127 // 遍历所有依赖当前组件 i 的其他组件 j
128 for &j in &adj[i] {
129 in_degree[j] -= 1; // j 的一个依赖已满足,入度 -1
130 if in_degree[j] == 0 { // 如果 j 的所有依赖都满足了
131 queue.push_back(j); // 将 j 加入队列等待处理
132 }
133 }
134 }
135
136 if result.len() != n {
137 let cycles: Vec<&str> = metas
138 .iter()
139 .enumerate()
140 .filter(|(i, _)| in_degree[*i] > 0)
141 .map(|(_, m)| m.name)
142 .collect();
143 panic!("[di] 循环依赖:{:?}", cycles);
144 }
145
146 let sorted_names: Vec<&str> = result
147 .iter()
148 .map(|t| {
149 id_to_idx.get(t).copied().unwrap_or_else(|| {
150 panic!("[di] 拓扑排序内部错误:TypeId {:?} 未在名称映射中找到", t)
151 })
152 })
153 .map(|(_, name)| name)
154 .collect();
155 debug!("[di] 拓扑排序结果:\n[\n{}\n]", sorted_names.join(",\n"));
156 let elapsed = start.elapsed();
157 debug!("[di] 拓扑排序耗时: {:?}", elapsed);
158
159 result
160}