1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//! 插件注册表:基于 [`inventory`] 的编译期收集 + 插件元数据。
//!
//! `#[command(...)]` 宏在保留原 `async fn` 与兄弟注册函数 `<fn>__nagisa_register`
//! 之外,额外 `inventory::submit!` 一个 [`TriggerSpec`]:把「元数据 + 挂载函数」
//! 登记到一个进程级集合里。启动时 [`collect_into`] 把所有 spec 的 `register`
//! 折叠到一个 [`Router`] 上,[`registered_triggers`] 则供 help/菜单插件列举命令。
//!
//! # DCE(死代码消除)注意事项
//!
//! `inventory` 依赖链接器把分散在各 crate 的静态注册项收集到一起。**若某插件
//! crate 从未被消费方 `use`(即整个 crate 对 bin 而言是死代码),链接器可能把它
//! 连同其 `inventory::submit!` 项一起剔除**——于是这些命令在运行期凭空消失,且
//! 不会有任何报错。
//!
//! 防御办法:**bin/app 必须既依赖插件 crate、又在源码里真实引用它**,让链接器
//! 保留该编译单元。常见两种写法:
//!
//! ```ignore
//! // 写法一:在 main.rs 顶部 glob-use 插件 crate(哪怕没用到任何名字)。
//! use my_plugins::*;
//!
//! // 写法二:插件 crate 暴露一个空的 `force_link()`,bin 显式调用一次。
//! pub fn force_link() {} // 在插件 crate 里
//! my_plugins::force_link(); // 在 bin 里
//! ```
//!
//! 同一编译单元内(如本 crate 的集成测试)不受此影响——只要测试里引用了被
//! `#[command]` 标注的函数(例如调用其 `<fn>__nagisa_register`),该单元就会被保留。
use crateTriggerMeta;
use crateRouter;
/// 一条 inventory 收集到的触发器:元数据 + 一个挂载 handler 的函数。
collect!;
/// 把每条收集到的 `TriggerSpec` 折叠到 `router` 上(顺序不稳定;先后由各触发器自身的
/// `priority` 决定,而非折叠顺序)。见上文 DCE 注意事项。
/// 全部已注册触发器的元数据(克隆)。供 help/菜单与插件分组使用。