thruput_rpc_api/lib.rs
1#![warn(missing_docs)]
2
3//! # thruput_rpc_api
4//!
5//! Stable API boundary for the Thruput ONC-RPC stack.
6//!
7//! This crate owns the shared public types that connect three parties:
8//!
9//! - **`thruput_rpc_driver`** — the transport, pool, and dispatch implementation.
10//! It depends on this crate, re-exports everything here at its historical
11//! paths, and implements [`RpcSender`] for its connections and pools.
12//! - **Generated protocol bindings** (protocol-rs clients and dispatchers),
13//! which are generated against this crate only and acquire no transport
14//! dependency.
15//! - **Consumers**, which wire generated clients and dispatchers to driver
16//! connections.
17//!
18//! Because all three share these types through a single, rarely-changing
19//! crate, `thruput_rpc_driver` can make breaking implementation-only releases
20//! (pools, configuration, transports) without forcing generated protocol
21//! crates to republish.
22//!
23//! ## Stability
24//!
25//! This crate is intentionally tiny, and its dependencies are limited to
26//! `async-trait` and `thiserror`. XDR serialization is the generated
27//! bindings' business: they encode through [`CallBuf::append_with`] with
28//! the `xdrs` they were generated against, so an `xdrs` release never
29//! forces a release here. Changes must be additive wherever
30//! possible: new [`RpcError`] variants are non-breaking thanks to
31//! `#[non_exhaustive]`, and a new trait method ships with a default
32//! implementation when a correct one exists for every sender. A breaking
33//! change here forces a coordinated republish of every generated protocol
34//! crate — treat it as a last resort.
35//!
36//! ## Sending a call
37//!
38//! Callers take a [`CallBuf`] from the sender with [`RpcSender::call_buf`],
39//! encode the arguments into it, and pass it to [`RpcSender::send_call`].
40//! The buffer keeps the wire prefix reserved ahead of the payload so the
41//! driver frames the call in place, and driver senders recycle spent record
42//! buffers through `call_buf`, so a steady stream of calls allocates
43//! nothing. [`CallBuf::append_with`] hands the record buffer to a closure
44//! that serializes into it in place; generated protocol clients encode
45//! their argument tuple that way.
46//!
47//! ```
48//! use thruput_rpc_api::CallBuf;
49//!
50//! let mut call = CallBuf::new();
51//! call.extend_from_slice(&42u32.to_be_bytes());
52//! assert_eq!(call.payload(), &[0, 0, 0, 42]);
53//! ```
54//!
55//! ## Chunked messages
56//!
57//! A message too large for one record goes out as a run of records that
58//! may arrive in any order. [`ChunkPosition`] says where a record sits in
59//! its run, and a protocol crate implements [`ChunkRecord`] for each of its
60//! chunked records; `thruput_rpc_driver::ChunkRunAssembler` reassembles the runs on
61//! the server, so handlers push the records they receive as they are.
62
63mod call_buf;
64mod chunk;
65mod error;
66mod handler;
67mod prog_error;
68mod response;
69mod sender;
70
71pub use call_buf::CallBuf;
72pub use chunk::{ChunkPosition, ChunkRecord};
73pub use error::RpcError;
74pub use handler::RpcHandler;
75pub use prog_error::ProgramError;
76pub use response::RpcResponse;
77pub use sender::RpcSender;
78
79/// RPC program and version identifier.
80#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
81pub struct ProgVers {
82 prog: u32,
83 vers: u32,
84}
85
86impl ProgVers {
87 /// Create a new program/version pair.
88 pub const fn new(prog: u32, vers: u32) -> Self {
89 Self { prog, vers }
90 }
91
92 /// Returns the program number.
93 pub fn prog(&self) -> u32 {
94 self.prog
95 }
96
97 /// Returns the version number.
98 pub fn vers(&self) -> u32 {
99 self.vers
100 }
101}