Skip to main content

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}