ek_core/plugin.rs
1//! `Plugin` trait —— 插件实现的唯一契约(业务与宿主的边界)。
2//!
3//! 纪律(写进 doc,实施由宿主强制):
4//! - 收:只有 [`Plugin::on_mail`] 一个入口;订阅面由 [`Plugin::routes`] 静态声明,
5//! 宿主 start() 时统一装配 forwarder(v0.1 不支持运行期动态订阅)。
6//! - 发:只有 [`PluginContext`](crate::PluginContext) 四个出口(put/ask/notify/write_log)。
7//! - 状态:插件状态放对象内部自锁(`Mutex/RwLock`);on_mail 在插件 worker 线程串行执行。
8//! - 停:on_stop 之后不再收到任何邮件(close 五步第②步)。
9
10use crate::context::PluginContext;
11use std::any::Any;
12use std::borrow::Cow;
13use std::time::Duration;
14
15use ek_event::Event;
16
17/// 默认邮箱容量(D5,confirms:按插件覆写override)。
18pub const MAILBOX_CAP: usize = 1024;
19
20/// 连续 panic 熔断阈值(D3)。
21pub const PANIC_LIMIT: u32 = 3;
22
23/// 插件名规则(D2):首字母字母,其余 alnum/_/-,长度 1..=32。
24pub fn valid_plugin_name(name: &str) -> bool {
25 let b = name.as_bytes();
26 if b.is_empty() || b.len() > 32 {
27 return false;
28 }
29 if !b[0].is_ascii_alphabetic() {
30 return false;
31 }
32 b[1..]
33 .iter()
34 .all(|c| c.is_ascii_alphanumeric() || *c == b'_' || *c == b'-')
35}
36
37pub trait Plugin: Any + Send + Sync {
38 /// 注册键(D2 校验)。
39 fn name(&self) -> &str;
40
41 /// D1 显式依赖清单(宿主注册期校验存在性,start 期拓扑排序)。
42 fn deps(&self) -> &[&str] {
43 &[]
44 }
45
46 /// 订阅的 type 集合:广播 type(如业务常量 "eTick.")、
47 /// 自身的寻址 type(e_req_type(本名)/e_to_type(本名))。
48 /// 宿主 start() 装配 forwarder → 邮箱 → on_mail。
49 fn routes(&self) -> Vec<Cow<'static, str>> {
50 Vec::new()
51 }
52
53 /// 邮箱容量覆写(D5:按插件可小可大,默认 [`MAILBOX_CAP`])。
54 fn mailbox_cap(&self) -> usize {
55 MAILBOX_CAP
56 }
57
58 /// 可选:启动前纯接线(缓存 ctx、读配置)。禁止在此 put/注册 handler(规范)。
59 fn on_init(&self, _ctx: &PluginContext) {}
60
61 /// start() 按依赖拓扑序调用(只一次)。
62 fn on_start(&self) {}
63
64 /// close/remove 按逆依赖序调用;调用后保证不再收到邮件。
65 fn on_stop(&self) {}
66
67 /// ★ 唯一业务入口:永远在本插件 worker 线程上串行执行。
68 /// 广播事件、寻址请求(eReq.<本名>)、定向通知(eTo.<本名>)都从这里进。
69 fn on_mail(&self, event: &Event);
70}
71
72/// close() 的 join 等待上限(D4:超时告警不死等)。
73pub const CLOSE_JOIN_TIMEOUT: Duration = Duration::from_secs(2);