Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Macros

rs-netty-macros 提供 #[handler]。主 crate 默认 feature 包含 macros,因此通常可以直接:

#![allow(unused)]
fn main() {
use rs_netty::handler;
}

What It Generates

#[handler(TypeName)] 把一个 async function 适配成 Handler<I>DatagramHandler<I> 两个 impl。用户仍然显式声明 handler struct,宏只生成样板代码。

一进一出:

#![allow(unused)]
fn main() {
struct Echo;

#[handler(Echo)]
async fn echo(msg: String) -> rs_netty::Result<String> {
    Ok(msg)
}
}

大致等价于:

#![allow(unused)]
fn main() {
impl rs_netty::Handler<String> for Echo {
    type Write = String;

    async fn read(
        &mut self,
        ctx: &mut rs_netty::Context<Self::Write>,
        msg: String,
    ) -> rs_netty::Result<()> {
        let msg = echo(msg).await?;
        ctx.write_and_flush(msg).await
    }
}
}

宏同时生成 DatagramHandler<String>,方法体使用 DatagramContext::write_and_flush

Consume-Only Handler

如果函数返回 Result<()>,宏无法从返回值推导 type Write,必须写 write = Type

#![allow(unused)]
fn main() {
struct PrintResponse;

#[handler(PrintResponse, write = String)]
async fn print_response(msg: String) -> rs_netty::Result<()> {
    println!("server -> {msg}");
    Ok(())
}
}

这里 write = String 的意思是这个 handler 所在连接仍然可以从外部 channel 写入 String

Handler State

需要访问 handler 字段时,把 &mut HandlerType 放在第一个参数:

#![allow(unused)]
fn main() {
struct PrintResponse {
    response_tx: Option<tokio::sync::oneshot::Sender<()>>,
}

#[handler(PrintResponse, write = Request)]
async fn print_response(handler: &mut PrintResponse, res: Response) -> rs_netty::Result<()> {
    if let Some(tx) = handler.response_tx.take() {
        let _ = tx.send(());
    }
    println!("server -> {}", res.echoed);
    Ok(())
}
}

这正是 examples/tcp_json_line_echo.rs 使用的模式。

Limits

宏要求:

  • 被标注的函数必须是 async fn
  • 函数必须返回 Result<T>
  • 参数最多是 (&mut HandlerType, msg)(msg)
  • write = Type 只允许用于返回 Result<()> 的函数。

需要直接操作 Context/DatagramContext、多次写入、手动 flush、关闭连接、拿 channel() 或分支复杂时,写手动 impl。