Skip to main content

ek_station/
wire.rs

1//! svctl —— 宿主管控服务的线上契约(唯一提供方 `crate::supervisor::Supervisor`)。
2//!
3//! 契约形状(与 ek-dm/ek-ctp 的 ask 同构,三口都走 `ask_service`):
4//! - [`SVC_LIST`]:[`ListReq`] → `Vec<[`PlugRow`]>`(全插件快照一行一个)。
5//! - [`SVC_START`]:[`StartReq`] → [`StartRes`](`Started`/`Rejected` 两种业务回执,
6//!   不用 `ReplyErr` 承载业务错 —— 回执枚举在总线上可读可断言)。
7//! - [`SVC_REMOVE`]:[`RemoveReq`] → [`RemoveRes`](`Removed`/`NoSuchName`/`Rejected`
8//!   三种业务回执;`name` 栏一律保真,错因只在 `Rejected.reason`)。
9//!
10//! 语义钉(ek-core 0.3.0 能力面自然修正):宿主没有"停而不摘表"的导出,
11//! 唯一的运行期下线动作是 `remove_plugin`(五步闭停 + 摘表 + `ePlugDown` 广播),
12//! 故 stop 语义并入 [`SVC_REMOVE`];再次启用走 [`SVC_START`] 动态重新注入
13//! (宿主 add_plugin 在 start 后直接 `launch_one` 上线并广播 `ePlugUp`)。
14
15use ek_core::service::ServiceDesc;
16
17// ================ 服务 id(服务目录键,UI 侧 ask_service 用) ================
18
19/// 列出全部插件快照。
20pub const SVC_LIST: &str = "svctl.list";
21/// 按工厂名动态注入一个新插件(start 后直接上线)。
22pub const SVC_START: &str = "svctl.start";
23/// 摘除一个运行中/未装配插件(stop 语义并入;ek-core 0.3.0 无"停而不摘表"导出)。
24pub const SVC_REMOVE: &str = "svctl.remove";
25
26// ================ list ================
27
28/// 一行插件快照(观测页逐行渲染)。
29#[derive(Debug, Clone, PartialEq)]
30pub struct PlugRow {
31    /// 注册名。
32    pub name: String,
33    /// 申报版本(插件自填;空串 = 未声明)。
34    pub version: String,
35    /// 生命周期状态(Debug 形状字符串:Ready/Running/Stopping/Exited/Quarantined)。
36    pub state: String,
37    /// 显式依赖的插件名。
38    pub deps: Vec<String>,
39    /// forwarder 溢出丢弃累计(邮箱满即丢,统计面显式可见)。
40    pub spilled: u64,
41    /// panic 累计(连续超限 → Quarantined)。
42    pub panics: u64,
43}
44
45/// 快照请求(无参)。
46#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
47pub struct ListReq;
48
49/// 快照回执(注册序)。
50pub type ListRes = Vec<PlugRow>;
51
52// ================ start ================
53
54/// 按工厂名注入请求。
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct StartReq {
57    /// 工厂名(= [`crate::factory::PlugKind`] 的 name)。
58    pub factory: String,
59    /// 实例注册名(多账号/多实例面)。
60    ///
61    /// 空 = 沿用旧行为(工厂名即注册名,单实例场景零回归)。
62    /// 非空 = 要求工厂以这个名字建实例(工厂须登记 `build_named`);
63    /// 名字合法性仍由宿主 `valid_plugin_name` 把关。
64    pub name: String,
65}
66
67/// 注入回执:业务结果自带(成功给出实际注册名;失败给原因)。
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub enum StartRes {
70    /// 已上线(宿主 add_plugin 成功,start 状态下立即装配)。
71    Started {
72        /// 插件注册名(= 插件自报的 name)。
73        name: String,
74    },
75    /// 拒绝(未知工厂/重名/名字非法 —— fix 后在 UI 侧红字呈现)。
76    Rejected {
77        /// 原因文本(Debug 形状,不进日志)。
78        reason: String,
79    },
80}
81
82// ================ remove ================
83
84/// 摘除请求(=ek-core `remove_plugin`;被依赖时宿主 stderr 警告,回执仍成功)。
85#[derive(Debug, Clone, PartialEq, Eq)]
86pub struct RemoveReq {
87    /// 注册名(非工厂名)。
88    pub name: String,
89}
90
91/// 摘除回执(三种业务回执,与 [`StartRes`] 同形;不用 `ReplyErr` 承载业务错)。
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub enum RemoveRes {
94    /// 已摘(五步闭停 + 摘表 + 广播 `ePlugDown`)。
95    Removed {
96        /// 被摘注册名。
97        name: String,
98    },
99    /// 表内无此名(宿主 `PluginError::UnknownName`)。
100    NoSuchName {
101        /// 原请求名(照请求回填,这一栏**不放错误文本**)。
102        name: String,
103    },
104    /// 名字在场但不可摘,或宿主另有错(0.2.11 新增,BD-UI-64)。
105    ///
106    /// 为什么要有这一档:0.2.10 之前非 `UnknownName` 的宿主错被塞进
107    /// [`NoSuchName`](RemoveRes::NoSuchName),而那一栏的 `name` 文档写明是
108    /// 「原请求名」—— 把 `format!("{e:?}")` 塞进去就是契约违约(消费侧
109    /// 会把错误文本当名字打日志,原请求名丢失)。
110    /// 现:名字一律保真,错误进 `reason`。
111    Rejected {
112        /// 原请求名(与请求一致)。
113        name: String,
114        /// 原因文本(Debug 形状或管制面自报的口径句)。
115        reason: String,
116    },
117}
118
119// ================ 服务目录自报(supervisor.rs 用) ================
120
121/// Supervisor 的三口目录(与 `crate::supervisor::Supervisor::services` 同源)。
122pub(crate) fn svctl_services() -> Vec<ServiceDesc> {
123    vec![
124        ServiceDesc::describe::<ListReq, ListRes>(SVC_LIST, "station: list plugin rows"),
125        ServiceDesc::describe::<StartReq, StartRes>(SVC_START, "station: inject plugin by factory"),
126        ServiceDesc::describe::<RemoveReq, RemoveRes>(SVC_REMOVE, "station: remove plugin by name"),
127    ]
128}