nub_host_common/rpc.rs
1//! Wire envelope for the host ↔ guest RPC.
2//!
3//! Both directions (host → guest function call, guest → host
4//! callback) use the same `Request` / `Response` shapes. The
5//! payload is opaque bytes — each `fn_id` defines its own inner
6//! codec (today: rkyv-archived domain types like
7//! `nub_arch_x86_abi::{InvocationSpec, InvocationResult}`).
8//!
9//! ## Wire layout
10//!
11//! Serializing a [`Request`] or [`Response`] with `rkyv::to_bytes`
12//! produces an `AlignedVec<u8>` that contains:
13//!
14//! 1. An archived `Request`/`Response` whose `payload` field is an
15//! `ArchivedVec<u8>` (the bytes laid out inline).
16//! 2. The rkyv root pointer trailer (a relative offset + length).
17//!
18//! Readers run `rkyv::access::<ArchivedRequest, _>(&bytes)` (with
19//! bytecheck) and pull `archived.payload.as_ref() -> &[u8]` to
20//! reach the inner payload — zero allocation, single pointer cast
21//! plus the cheap bytecheck pass.
22//!
23//! Direction is encoded by which shared-memory ring the bytes live
24//! in (host's input vs output ring), not by the type.
25
26use alloc::string::String;
27use alloc::vec::Vec;
28
29/// Host → guest function call OR guest → host callback. The
30/// `payload` is opaque to the envelope: the fn_id selects what
31/// inner codec applies.
32#[derive(Debug, Clone, PartialEq, Eq, rkyv::Archive, rkyv::Serialize)]
33#[rkyv(derive(Debug))]
34pub struct Request {
35 /// Compile-time-assigned identifier for the target function.
36 /// Receiver matches on this to route to the right handler.
37 pub fn_id: u32,
38 /// Opaque payload bytes — the fn-specific codec lives inside.
39 pub payload: Vec<u8>,
40}
41
42/// Reply to a [`Request`]. `status == 0` means OK; non-zero is a
43/// receiver-defined error code with the human-readable detail in
44/// `error_msg` (mostly for debugging).
45///
46/// On OK, `payload` holds the function's archived return value.
47/// On error, `payload` is typically empty but the field is kept
48/// for symmetry / future use.
49#[derive(Debug, Clone, PartialEq, Eq, rkyv::Archive, rkyv::Serialize)]
50#[rkyv(derive(Debug))]
51pub struct Response {
52 /// 0 = ok; non-zero = error (handler-specific code).
53 pub status: u32,
54 /// Human-readable error detail on non-zero status.
55 pub error_msg: Option<String>,
56 /// Archived return-value bytes on success; empty on error.
57 pub payload: Vec<u8>,
58}
59
60impl Response {
61 /// Construct a success response with the given archived payload.
62 pub fn ok(payload: Vec<u8>) -> Self {
63 Self {
64 status: 0,
65 error_msg: None,
66 payload,
67 }
68 }
69
70 /// Construct an error response. `status` must be non-zero.
71 pub fn err(status: u32, msg: impl Into<String>) -> Self {
72 debug_assert!(status != 0, "Response::err with status=0 is OK, not error");
73 Self {
74 status,
75 error_msg: Some(msg.into()),
76 payload: Vec::new(),
77 }
78 }
79}