rpc-service
A typed RPC server using shared-memory FBMU as the transport. Client serializes a EchoRequest to a leased queue; server pulls, dispatches, returns EchoResponse on the response queue. No TLS, no HTTP — the lease is the trust boundary.
Source
cookbook/rpc-service/ in the source tree.
The recipe is the host-testable handler: take a typed EchoRequest, return a typed EchoResponse. In production, grafos-rpc::service macro generates the request/response wire types, server adapter, and client adapter.
#[serde(tag = "method", content = "params", rename_all = "snake_case")]pub enum EchoRequest { Ping, Echo { message: String }, Reverse { message: String },}
pub fn handle(req: EchoRequest) -> EchoResponse { match req { EchoRequest::Ping => EchoResponse::Pong, EchoRequest::Echo { message } => { if message.len() > 1024 { EchoResponse::Error { reason: "message_too_large".into() } } else { EchoResponse::Echoed { message } } } EchoRequest::Reverse { message } => { EchoResponse::Reversed { message: message.chars().rev().collect() } } }}What’s interesting
- Tagged JSON discriminator.
#[serde(tag = "method", content = "params")]produces wire shapes like{"method":"echo","params":{"message":"hi"}}. Versioning a method is renaming it; old clients see a typedError { reason: "method_not_supported" }. - Errors are values.
EchoResponse::Error { reason }is part of the response enum, not an out-of-band error path. The client deserializes one variant or another; there’s no “is the response valid?” check separate from “what response is it?” - Bounded request sizes.
Echorejects messages over 1024 bytes; the cap is part of the API contract, not a runtime fluke. - Production drop-in. Replace the manual
matchwith#[grafos_rpc::service]on a trait that has one method perEchoRequestvariant. The macro generates the dispatch + queue plumbing.