Skip to main content

cratestack_axum/rpc/
mod.rs

1//! Runtime primitives for the `transport rpc` generation style.
2//!
3//! See `docs/design/rpc-transport.md` for the full design. This module
4//! provides the binding-side surface that schemas with `transport rpc`
5//! generate against:
6//!
7//! - `POST /rpc/{op_id}` — unary calls. Body is the codec-encoded *input*
8//!   (no frame wrapper); response body is the codec-encoded *output* on
9//!   success, or an [`RpcErrorBody`] on error with HTTP status mapped via
10//!   [`CratestackError::status_code`].
11//! - `POST /rpc/batch` — sequence of `RpcRequest` frames in, sequence of
12//!   `RpcResponseFrame` frames out in the same order. Per-frame errors
13//!   don't poison the batch.
14//! - `GET /rpc/subscribe/{op_id}` — SSE subscription dispatch for
15//!   `@@subscribe`d models (design doc §3.4a, cratestack#390). One
16//!   `event: message` per `ModelEvent<T>`, terminated by one
17//!   `event: error` on backpressure overflow. See [`sse`] and
18//!   [`subscription_bridge`].
19//!
20//! The full WebSocket frame loop (§3.4) remains speced but unbuilt,
21//! gated on a real bidirectional/high-multiplexing need per issue
22//! #183's spike decision — see `docs/design/rpc-transport.md` §6.5.
23//!
24//! The macro emits the dispatch table and the `rpc_router` constructor.
25//! This crate provides the shared frame shapes, error mapping, and the
26//! `RPC_*_PATH` constants both sides agree on.
27
28mod batch;
29mod codec_helpers;
30mod error_encode;
31mod sse;
32mod subscription_bridge;
33mod synthesize;
34mod util;
35
36#[cfg(test)]
37mod tests_error;
38#[cfg(test)]
39mod tests_frame;
40#[cfg(test)]
41mod tests_get;
42#[cfg(test)]
43mod tests_list;
44#[cfg(test)]
45mod tests_response_rebuffer;
46
47// Re-export the wire shapes from `cratestack-core::rpc`. Both the server
48// binding and every generated client agree on those shapes, and lifting
49// them into core means the client crates don't need to depend on axum.
50// `RpcListInput`/`RpcListPredicate`/`RpcPkInput`/`RpcUpdateInput` joined
51// this list via cratestack#490 — previously defined locally in this
52// crate's own (now-removed) `inputs` module, which meant
53// `include_client_schema!`'s RPC model-CRUD codegen (`::cratestack::rpc::
54// RpcListInput`, …) could never resolve for a facade without
55// `cratestack-axum` in its graph. See `cratestack-core::rpc`'s doc comment
56// on those types for the full story.
57pub use cratestack_core::rpc::{
58    RPC_BATCH_PATH, RPC_STREAM_ERROR_TAG, RPC_SUBSCRIBE_PATH, RPC_UNARY_PATH, RpcErrorBody,
59    RpcGetInput, RpcListInput, RpcListPredicate, RpcPkInput, RpcRequest, RpcResponseFrame,
60    RpcUpdateInput, cratestack_error_code_to_rpc_code, rpc_code,
61};
62
63pub use batch::response_to_frame;
64pub use codec_helpers::{decode_rpc_body, encode_rpc_value};
65pub use error_encode::{convert_handler_error_response, encode_rpc_error};
66pub use sse::{encode_model_event_sse_response, validate_subscribe_accept_header};
67pub use subscription_bridge::{SubscriptionPush, guarded_receiver_stream, subscription_channel};
68pub use synthesize::{synthesize_get_query, synthesize_list_query};
69
70/// Codec/transport capabilities for every RPC binding route. Both unary
71/// and batch accept and emit CBOR or JSON, default CBOR; sequence
72/// responses (streaming) are not yet supported by this binding.
73///
74/// Used by `encode_transport_result_with_status_for` to negotiate
75/// response content type when the dispatcher synthesizes an error
76/// response or wraps a batch result.
77pub const RPC_BINDING_CAPABILITIES: cratestack_core::RouteTransportCapabilities =
78    cratestack_core::RouteTransportCapabilities {
79        request_types: &["application/cbor", "application/json"],
80        response_types: &["application/cbor", "application/json"],
81        default_response_type: "application/cbor",
82        supports_sequence_response: false,
83    };