@@ -55,10 +55,10 @@ async fn main() {
5555初始化结果封装在 ` Initialize ` 结构体中,它的定义如下:
5656
5757``` rust
58- pub struct Initialize <C > {
58+ pub struct Initialize <C = () > {
5959 pub config : C ,
60- pub data_path : String ,
6160 pub id : String ,
61+ pub data_path : String ,
6262}
6363```
6464
@@ -67,8 +67,8 @@ pub struct Initialize<C> {
6767这个结构体包含了两个重要字段:
6868
6969- ` config ` 字段表示插件的配置信息,可以为任何可反序列化类型
70- - ` data_path ` 字段表示插件的数据路径,我们约定插件的所有数据都存储在该路径下
7170- ` id ` 字段表示插件的唯一标识符,用于在框架中区分不同的插件实例。
71+ - ` data_path ` 字段表示插件的数据路径,我们约定插件的所有数据都存储在该路径下
7272
7373</Callout >
7474
@@ -77,6 +77,7 @@ pub struct Initialize<C> {
7777通过为 ` plugin! ` 宏指定泛型参数,我们可以定义插件专属的配置类型:
7878
7979``` rust
80+ #[derive(Debug , Clone , Deserialize )]
8081struct Config {
8182 admins : Vec <String >,
8283}
@@ -112,6 +113,30 @@ tokio::select! {
112113}
113114```
114115
116+ ### 错误处理
117+
118+ 在初始化过程中,插件应捕获并处理所有错误。` plugin ` 提供了以下方法来响应初始化状态:
119+
120+ - ` plugin.err(error) ` : 响应错误并终止初始化过程(返回 ` never ` )。
121+ - ` plugin.ok() ` : 响应初始化完成消息并允许插件继续执行。
122+ - ` plugin.expect(result) ` : 这是一个特殊方法。如果 ` result ` 是 ` Err(error) ` ,其行为与调用 ` plugin.err(error) ` 一致,终止初始化。如果 ` result ` 是 ` Ok(value) ` ,则返回 ` value ` 并继续初始化。
123+
124+ 签名:
125+ ``` rust
126+ type Self = Plugin ;
127+
128+ /// Send an initialization response indicating failure.
129+ async fn err (& mut self , err : impl Into <PluginInitError >) -> ! ;
130+ /// Send an initialization response indicating success.
131+ fn ok (& mut self );
132+ /// Tool used to handle errors and continue initialization.
133+ async fn expect <T , E : Display >(& mut self , r : Result <T , E >) -> T ;
134+ ```
135+
136+ ** 隐式行为:**
137+
138+ 如果在调用 ` plugin.run() ` 之前没有显式地通过 ` plugin.ok() ` 或 ` plugin.err() ` 确认初始化状态,框架将自动调用 ` plugin.ok() ` 来确认初始化完成。一旦 ` plugin.ok() ` 被调用,框架将停止等待初始化消息,并将插件视为已成功启动,不再捕获初始化错误信息。因此,除非有特殊需求,否则不建议手动调用 ` plugin.ok() ` 来完成初始化。
139+
115140<Callout type = " warn" icon = { <Terminal />} >
116141
117142当插件退出时,框架内部会直接丢弃插件实例,析构时插件会被 kill。
@@ -151,9 +176,12 @@ tokio::select! {
151176
152177``` rust
153178pub struct Plugin {
154- peer : Peer ,
155- server : Server ,
156- router : Router ,
179+ peer : Peer ,
180+ pub server : Server ,
181+ router : Router ,
182+ name : String ,
183+ version : String ,
184+ initialize_done : bool ,
157185}
158186```
159187
@@ -174,22 +202,19 @@ pub struct Plugin {
174202让我们拆解 ` Plugin::new ` 方法,了解其内部实现:
175203
176204``` rust
177- pub async fn new <Config >(version : & str , name : & str ) -> (Self , Initialize <Config >)
205+ pub async fn new <Config , F >(version : & str , name : & str , init_fn : F ) -> (Self , Initialize <Config >)
178206where
179207 Config : for <'de > Deserialize <'de >,
208+ F : FnOnce (& mut Initialize <Config >) -> InitializeResult ,
180209{
181- // 处理命令行选项,如 --version
182210 if handle_options (version , name ) {
183211 process :: exit (0 );
184212 }
185- // 创建对等节点、服务端和路由表
186213 let peer = Peer :: new ();
187214 let server = Server :: new ();
188215 let router = Router :: new ();
189- // 从对等节点创建帧序列
190216 let mut framed = crate :: transport :: util :: framed (peer );
191217
192- // 执行初始化循环,等待框架发送初始化消息
193218 let init = loop {
194219 let Some (msg ) = <FramedPeer as StreamExt >:: next (& mut framed ). await else {
195220 break Err (PluginInitError :: ConnectionClosed );
@@ -203,47 +228,46 @@ where
203228 }
204229 };
205230
206- // 处理初始化结果
207- let init = match init {
231+ let mut init = match init {
208232 Ok (init ) => init ,
209233 Err (err ) => {
210- // 发送错误信息给框架
211234 framed
212235 . send (
213236 DataPack :: builder ()
214237 . path (Initialize :: <Config >:: path ())
215- . build_with_payload (InitializeResult :: Err (err )),
238+ . build_with_payload (& InitializeResult :: Err (err )),
216239 )
217- . await . ok ();
218- // 等待退出信号
240+ . await
241+ . ok ();
219242 tokio :: signal :: ctrl_c (). await . ok ();
220- // 退出进程
221243 process :: exit (1 );
222244 }
223245 };
224246
225- // 初始化日志系统
226247 init_log (server . client (). sink ());
227248
228- // 发送初始化成功响应给框架
229- server
230- . client ()
231- . send (
232- RequestDataPack :: default ()
233- . path (Initialize :: <Config >:: path ())
234- . payload (InitializeResult :: Ok (())),
235- )
236- . unwrap_or_else (| _ | panic! (" Failed to send initialization response: [{name}]" ));
249+ if let Err (err ) = init_fn (& mut init ) {
250+ framed
251+ . send (
252+ DataPack :: builder ()
253+ . path (Initialize :: <Config >:: path ())
254+ . build_with_payload (& InitializeResult :: Err (err )),
255+ )
256+ . await
257+ . ok ();
258+ tokio :: signal :: ctrl_c (). await . ok ();
259+ process :: exit (1 );
260+ }
237261
238- // 用不到帧序列了,我们换回对等节点
239262 let peer = framed . into_inner ();
240-
241- // 返回实例
242263 (
243264 Self {
244265 peer ,
245266 server ,
246267 router ,
268+ name : name . to_owned (),
269+ version : version . to_owned (),
270+ initialize_done : false ,
247271 },
248272 init ,
249273 )
@@ -259,7 +283,7 @@ where
2592833 . 等待并解析框架的初始化消息
2602844 . 如果初始化失败,向框架报告错误并退出
2612855 . 初始化日志系统
262- 6 . 向框架发送初始化成功响应
286+ 6 . 执行用户提供的初始化函数
2632877 . 返回插件实例和初始化结果
264288
265289</Callout >
@@ -269,11 +293,16 @@ where
269293` Plugin::run ` 方法的实现相对简洁:
270294
271295``` rust
272- pub fn run (self ) -> JoinSet <Result <(), ServerError >> {
296+ pub fn run (mut self ) -> JoinSet <Result <(), ServerError >> {
297+ // 如果初始化未完成,则隐式发送成功响应
298+ if ! self . initialize_done {
299+ self . ok ();
300+ }
273301 let Self {
274302 peer ,
275303 server ,
276304 router ,
305+ ..
277306 } = self ;
278307 // 分割对等节点为写入和读取部分
279308 let (write , read ) = peer . split ();
@@ -345,16 +374,8 @@ pub fn run(self) -> JoinSet<Result<(), ServerError>> {
345374
346375 // 初始化日志
347376 init_log (server . client (). sink ());
348-
349- // 发送初始化成功响应
350- server
351- . client ()
352- . send (
353- RequestDataPack :: default ()
354- . path (Initialize :: <Config >:: path ())
355- . payload (InitializeResult :: Ok (())),
356- )
357- . unwrap_or_else (| _ | panic! (" Failed to send initialization response: [{NAME}]" ));
377+
378+
358379
359380 // 用不到帧序列了,我们换回对等节点
360381 let peer = framed . into_inner ();
@@ -413,7 +434,13 @@ pub fn run(self) -> JoinSet<Result<(), ServerError>> {
413434
414435 // 初始化日志
415436 init_log (server . client (). sink ());
416-
437+
438+ // 这里是自定义初始化逻辑的地方,对应 `plugin!` 宏的第二个参数
439+ // 例如:
440+ // if let Err(e) = some_init_task(&init.config).await {
441+ // // report error and exit
442+ // }
443+
417444 // 发送初始化成功响应
418445 server
419446 . client ()
@@ -458,4 +485,4 @@ pub fn run(self) -> JoinSet<Result<(), ServerError>> {
458485恭喜您已经深入了解了 sithra-rs 插件的初始化流程!接下来,您可以:
459486
460487- 了解如何[ 注册和处理路由] ( /docs/development/router )
461- - 探索[ 内部服务] ( /docs/development/server ) 的实现和使用
488+ - 探索[ 内部服务] ( /docs/development/server ) 的实现和使用
0 commit comments