beryl/wire/codec
Pluggable wire codec for beryl.
A Codec plugs the coordinator into any over-the-wire framing. The
canonical implementation is beryl/wire.phoenix_codec(), which ships
the Phoenix array format ([join_ref, ref, topic, event, payload]).
To run beryl over your own framing, build a Codec value and pass it
to beryl.config(codec). The coordinator decodes inbound text via
codec.decode_text, optionally decodes inbound binary via
codec.decode_binary, dispatches based on the structural InboundKind,
and produces outbound text or binary frames via codec.encode_* helpers.
All codecs must normalise inbound traffic to the Inbound shape so
the coordinator can stay framing-agnostic.
A wire codec.
Codec is opaque; build one with new (and, for binary support,
with_binary_decoder). The coordinator reads the codec's behaviour
through the @internal accessors below.
pub type CodecDecodeError
Section titled “DecodeError”Errors a codec may emit when decoding inbound bytes.
pub type DecodeError { InvalidJson(reason: String) InvalidFormat(reason: String) MissingField(name: String)}Constructors
Section titled “Constructors”InvalidJson(reason: String)
Section titled “InvalidJson(reason: String)”The bytes were not valid JSON; reason describes the parse error.
InvalidFormat(reason: String)
Section titled “InvalidFormat(reason: String)”The message was valid JSON but did not match the expected framing;
reason describes the mismatch.
MissingField(name: String)
Section titled “MissingField(name: String)”A required field was absent; name is the missing field.
Encoded WebSocket frame returned by a codec.
pub type Frame { TextFrame(String) BinaryFrame(BitArray)}Constructors
Section titled “Constructors”TextFrame(String)
Section titled “TextFrame(String)”A UTF-8 text frame.
BinaryFrame(BitArray)
Section titled “BinaryFrame(BitArray)”A binary frame.
Inbound
Section titled “Inbound”Normalised inbound message shape.
Inbound is opaque: construct it with inbound and read it with the
inbound_* accessors. Keeping the record hidden lets beryl add fields
(which default sensibly) without breaking every custom codec.
pub type InboundInboundKind
Section titled “InboundKind”Structural inbound message kind used for protocol dispatch.
pub type InboundKind { Join Leave Heartbeat Event(String)}Constructors
Section titled “Constructors”A client joining a topic.
A client leaving a topic.
Heartbeat
Section titled “Heartbeat”A heartbeat/keep-alive message.
Event(String)
Section titled “Event(String)”A user-defined event; the wrapped String is the event name.
ReplyStatus
Section titled “ReplyStatus”Status of a reply produced by a channel callback.
pub type ReplyStatus { StatusOk StatusError}Constructors
Section titled “Constructors”StatusOk
Section titled “StatusOk”The callback succeeded ("ok" in Phoenix framing).
StatusError
Section titled “StatusError”The callback failed ("error" in Phoenix framing).
Functions
Section titled “Functions”format_decode_error
Section titled “format_decode_error”Format a DecodeError as a human-readable string. Used by the
coordinator's log messages and by wire.format_decode_error.
pub fn format_decode_error(DecodeError) -> Stringinbound
Section titled “inbound”Construct a normalised inbound message.
join_ref: optional client-side reference assigned at join time (used by some Phoenix replies; codecs without this concept should passNone)ref: optional per-message reference for reply correlationtopic: subscription topic (e.g."room:lobby","doc:abc")kind: structural protocol event or user eventpayload: message body as aDynamicfor the channel callback to decode
pub fn inbound( join_ref: option.Option(String), ref: option.Option(String), topic: String, kind: InboundKind, payload: dynamic.Dynamic) -> Inboundinbound_join_ref
Section titled “inbound_join_ref”The inbound message's join-time client reference, if any.
pub fn inbound_join_ref(Inbound) -> option.Option(String)inbound_kind
Section titled “inbound_kind”The inbound message's structural kind.
pub fn inbound_kind(Inbound) -> InboundKindinbound_payload
Section titled “inbound_payload”The inbound message's body, for the channel callback to decode.
pub fn inbound_payload(Inbound) -> dynamic.Dynamicinbound_ref
Section titled “inbound_ref”The inbound message's per-message reference for reply correlation, if any.
pub fn inbound_ref(Inbound) -> option.Option(String)inbound_topic
Section titled “inbound_topic”The inbound message's subscription topic.
pub fn inbound_topic(Inbound) -> StringBuild a text-only wire codec.
decode_text: decode raw inbound text into a normalisedInbound.encode_reply: encode a reply to a client message:(join_ref, ref, topic, status, response_payload).encode_push: encode a server-initiated push:(topic, event, payload).encode_heartbeat_reply: encode a heartbeat reply for a given clientref.
The resulting codec has no binary decoder; binary WebSocket frames are
routed to channel.handle_binary as raw data. Add a binary decoder with
with_binary_decoder.
pub fn new( decode_text: fn(String) -> Result(Inbound, DecodeError), encode_reply: fn(option.Option(String), option.Option(String), String, ReplyStatus, json.Json) -> Frame, encode_push: fn(String, String, json.Json) -> Frame, encode_heartbeat_reply: fn(option.Option(String)) -> Frame) -> Codecwith_binary_decoder
Section titled “with_binary_decoder”Attach a binary decoder to a codec.
When set, binary WebSocket frames are decoded into a normalised Inbound
via decode_binary instead of being routed to channel.handle_binary as
raw data.
pub fn with_binary_decoder( Codec, fn(BitArray) -> Result(Inbound, DecodeError)) -> Codecwith_close_encoder
Section titled “with_close_encoder”Attach a channel-close encoder to a codec.
When set, the coordinator emits this frame to a client whenever one of
its channels terminates gracefully (leave, server shutdown, heartbeat
eviction): (join_ref, topic). Phoenix clients rely on phx_close to
leave the joined state instead of waiting out push timeouts.
pub fn with_close_encoder( Codec, fn(option.Option(String), String) -> Frame) -> Codecwith_error_encoder
Section titled “with_error_encoder”Attach a channel-error encoder to a codec.
When set, the coordinator emits this frame to a client whenever one of
its channels terminates abnormally (crashed or stopped with an error):
(join_ref, topic). Phoenix clients rely on phx_error to schedule an
automatic rejoin.
pub fn with_error_encoder( Codec, fn(option.Option(String), String) -> Frame) -> Codecwith_topicless_events
Section titled “with_topicless_events”Mark a codec's events as topicless.
Some framings (e.g. Socket.IO-style protocols) do not carry a per-frame topic. When set, an inbound event whose topic is empty is routed to the socket's single joined topic; with zero or multiple joins it is dropped. Topic-carrying codecs (like the Phoenix codec) must leave this off so empty-topic frames are rejected instead of guessed at.
pub fn with_topicless_events(Codec) -> Codec