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 provides #[handler]. The main crate’s default features include macros, so in most cases you can write:

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

What It Generates

#[handler(TypeName)] adapts one async function into both Handler<I> and DatagramHandler<I> impls. The user still declares the handler struct explicitly; the macro only generates repetitive implementation code.

Request-to-response handler:

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

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

This is roughly equivalent to:

#![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
    }
}
}

The macro also generates DatagramHandler<String> with DatagramContext::write_and_flush.

Consume-Only Handler

If the function returns Result<()>, the macro cannot infer type Write from the return type. You must specify 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(())
}
}

Here write = String means the connection can still write String values from an external channel.

Handler State

To access handler fields, put &mut HandlerType as the first argument:

#![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(())
}
}

This is the pattern used by examples/tcp_json_line_echo.rs.

Limits

The macro requires:

  • the annotated function must be async fn.
  • the function must return Result<T>.
  • arguments must be either (&mut HandlerType, msg) or (msg).
  • write = Type is allowed only for functions returning Result<()>.

Use a manual impl when you need direct Context/DatagramContext access, multiple writes, manual flushes, connection close, channel(), or more complex branching.