pub enum Frame {
Handshake {
version: ProtocolVersion,
},
HandshakeAck {
version: ProtocolVersion,
},
Request {
id: OperationId,
ops: String,
deadline_ms: Option<u64>,
namespace: Option<String>,
actor_id: Option<String>,
visible_namespaces: Option<Vec<String>>,
},
Response {
id: OperationId,
result: Value,
},
Error {
id: Option<OperationId>,
code: WireErrorCode,
message: String,
unrecognized_code: Option<String>,
},
Cancel {
id: OperationId,
},
Subscribe {
id: OperationId,
topic: String,
resume_cursor: Option<Cursor>,
},
SubscribeAck {
id: OperationId,
topic: String,
start_cursor: Cursor,
},
Unsubscribe {
id: OperationId,
topic: String,
},
UnsubscribeAck {
id: OperationId,
topic: String,
},
Event {
topic: String,
cursor: Cursor,
occurred_at: String,
payload: Value,
},
}Expand description
One application frame.
Every variant corresponds to exactly one entry in FRAME_KINDS and is
carried on the wire as a JSON object with a "kind" discriminant field
holding that variant’s snake_case name, followed by the variant’s own
fields flattened into the same object. This is the wire framing’s
internally tagged encoding; see the crate documentation for a worked
example of the exact bytes.
The set is closed within a protocol version (ADR-137, “Decision”): a
decoder that encounters a "kind" value outside FRAME_KINDS must
reject the frame (crate::codec::CodecError::UnknownFrameKind), never
skip or ignore it.
Serde is implemented by hand (rather than #[serde(tag = "kind")]) so
the decode path can enforce the closed grammar ADR-137 requires: the
codec checks the "kind" discriminant against FRAME_KINDS itself,
then hands the payload to the matching kind’s payload struct
(HandshakePayload, RequestPayload, …), every one of which
carries #[serde(deny_unknown_fields)]. A payload with any field its
kind does not declare is therefore rejected — never silently ignored —
and a missing or non-string "kind" is rejected before any kind is
matched. The visitor likewise enforces the ADR-137 id/scope rule for
error frames and captures the unknown-code fallback’s raw string
(Frame::Error’s unrecognized_code), so a DIRECT serde
decode of Frame — not just the codec — can never represent an
inconsistent error frame. Encoding writes "kind" first, then the
kind’s fields in
declaration order, skipping absent optional fields; this is the exact
byte layout the golden fixtures pin.
Variants§
Handshake
The first application frame on every connection. Names the protocol version the client supports.
Fields
version: ProtocolVersionThe protocol version the client wants to speak.
HandshakeAck
The server’s acceptance of a Frame::Handshake, naming the
accepted protocol version.
Fields
version: ProtocolVersionThe protocol version the connection now speaks.
Request
A caller-issued operation: a DSL batch or chain (ADR-016) to execute.
Fields
id: OperationIdCaller-generated, connection-unique operation id.
deadline_ms: Option<u64>Optional deadline in milliseconds, measured from server receipt of this frame against the server’s monotonic clock. Scopes the entire request frame (the whole DSL batch or chain).
namespace: Option<String>Frame-level namespace override. Legal only on transports that
accept caller-supplied identity context; a mapped transport
(ADR-137’s TCP transport) rejects any request carrying this with
crate::error::WireErrorCode::ContextRejected.
Response
The successful terminal frame for a request.
Fields
id: OperationIdEchoes the originating request’s operation id.
Error
A wire-level failure terminal frame.
Fields
id: Option<OperationId>The operation id this error terminates, for a request-scoped
error. None for a connection-terminal error, which carries no
operation id (ADR-137, “Operation correlation”).
code: WireErrorCodeThe wire error code.
message: StringA human-readable detail message. Not part of the closed
contract — callers must branch on code, never on this string.
unrecognized_code: Option<String>The raw code string when the wire carried a code OUTSIDE the
closed set (crate::error::WIRE_ERROR_CODES) and serde’s
#[serde(other)] fallback mapped it to
crate::error::WireErrorCode::Internal; None for every
recognized code, and for every frame this crate produced by
encoding or by in-memory construction. Diagnostic only: it is
never serialized, and only DECODE paths fill it — this type’s
serde visitor, whether driven by the codec’s decode_payload
or by a direct serde decode. A frame carrying it is a decoded
fallback and cannot be re-encoded by this relay: the encode path
rejects it (crate::codec::CodecError::FallbackFrameNotEncodable)
rather than emit internal and silently discard the newer code.
The connection remains healthy; only this relay attempt failed.
If it has no operation id, a consumer should surface it as a
connection-level diagnostic rather than guess which request to
fail.
Cancel
Asks the server to terminate an in-flight request.
Fields
id: OperationIdThe request operation id to cancel. A cancel naming a
subscribe/unsubscribe id, or an unknown or already-terminal
request id, is a no-op.
Subscribe
Opens delivery for one topic on the connection.
Fields
id: OperationIdCaller-generated, connection-unique operation id.
SubscribeAck
The successful terminal frame for a subscribe.
Fields
id: OperationIdEchoes the originating subscribe’s operation id.
Unsubscribe
Ends delivery for one topic on the connection.
Fields
id: OperationIdCaller-generated, connection-unique operation id.
UnsubscribeAck
The terminal frame for an unsubscribe.
Fields
id: OperationIdEchoes the originating unsubscribe’s operation id.
Event
A server-pushed state-change delivery for a subscribed topic.
Carries no operation id; correlated by topic and ordered by cursor instead.
Fields
Implementations§
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Frame
The decode half of the closed grammar; see the type-level docs and the
crate documentation’s “Strict field rejection” section. The codec’s
decode_payload drives this via serde_json::from_value::<Frame>
after its own closed-set "kind" check (which produces the
finer-grained crate::codec::CodecError::UnknownFrameKind).
impl<'de> Deserialize<'de> for Frame
The decode half of the closed grammar; see the type-level docs and the
crate documentation’s “Strict field rejection” section. The codec’s
decode_payload drives this via serde_json::from_value::<Frame>
after its own closed-set "kind" check (which produces the
finer-grained crate::codec::CodecError::UnknownFrameKind).
The visitor enforces every decode-time rule that only needs the fields
of one frame, so a DIRECT serde decode of Frame (e.g.
serde_json::from_str::<Frame>) agrees with the codec path:
- the ADR-137 id/scope pairing for
errorframes — a connection-terminal code must carry no operation id, and a request-terminal code must echo the one it terminates (crate::codec::CodecError::InconsistentErrorScopeis the codec’s typed form of this rejection; a direct serde decode reports the same rule through its deserializer’s error type); and - the unknown-code fallback diagnostic: an
errorframe whose wire code is outside the closed set (crate::error::WIRE_ERROR_CODES) carries the raw string inunrecognized_code.
The one guarantee only the codec path provides is the finer-grained
crate::codec::CodecError::UnknownFrameKind classification for a
"kind" outside FRAME_KINDS; this visitor rejects unknown kinds
too, with the deserializer’s own error type.
Source§fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>
Source§impl Serialize for Frame
Serialize one frame as its wire object: "kind" first, then the kind’s
fields in declaration order, absent optional fields skipped. The byte
layout is pinned by the golden fixtures in tests/fixtures/*.hex.
impl Serialize for Frame
Serialize one frame as its wire object: "kind" first, then the kind’s
fields in declaration order, absent optional fields skipped. The byte
layout is pinned by the golden fixtures in tests/fixtures/*.hex.