pub struct SseEvent { /* private fields */ }Expand description
One text/event-stream frame.
id is optional because the format says so: a frame without an id: is
legal SSE and dispatches normally, it simply does not move the client’s
resume cursor. That is the shape a keep-alive-adjacent event takes, and an
encoder that always wrote an id would either invent a cursor position
that never existed or refuse to emit the frame at all.
name is optional for the same reason, and for a sharper one: Streamable
HTTP never sets it — every frame on that transport is a message and the
name carries nothing. The 2024-11-05 transport names every frame, and a
client that dispatches on the name is the only way to tell an endpoint
frame (where to POST) from a message frame (a JSON-RPC payload). A codec
that dropped the name would parse a legacy stream perfectly and hand the
client two indistinguishable event kinds.
A frame with no data does not exist as a value: the standard dispatches
nothing for such a block, so decode reports
SseError::NoData and dispatch reports None.
Implementations§
Source§impl SseEvent
impl SseEvent
Sourcepub const fn without_id(data: String) -> Self
pub const fn without_id(data: String) -> Self
A frame with no resume cursor, which leaves the client’s where it was.
Sourcepub const fn named(name: EventName, data: String) -> Self
pub const fn named(name: EventName, data: String) -> Self
A named frame, for the transports that dispatch on the name.
Takes an EventName rather than a &str because a name is spliced
into a line and a line can be terminated from inside it — the injection
is a parse away, and EventName is the parse that closes it.
Constructed from a &'static str the way the legacy transport’s two
names are; a dynamic name goes through EventName::from_str.
A named frame carries no resume cursor, and that is a property of the
transport rather than a restriction: 2024-11-05 has no Last-Event-ID
and no replay, so a name is never combined with a cursor on this
transport. The two are still representable together for a future
transport that does name and resume at once.
Sourcepub const fn named_with_id(name: EventName, id: EventId, data: String) -> Self
pub const fn named_with_id(name: EventName, id: EventId, data: String) -> Self
A named frame that also carries a resume cursor.
Sourcepub fn json(id: EventId, value: &Value) -> Result<Self, SseError>
pub fn json(id: EventId, value: &Value) -> Result<Self, SseError>
A frame whose data is value serialised as JSON.
The only constructor that takes structured data, and the reason the
Debug-formatting bug is unreachable rather than merely discouraged —
see the module docs.
§Errors
SseError::Unserialisable if the value cannot be serialised, which
for a Value means a non-string map key. The payload is the message
the peer receives, so an event that cannot be encoded must not be
encoded wrong.
Sourcepub fn into_data(self) -> String
pub fn into_data(self) -> String
Consumes the frame, yielding the payload.
The id is dropped rather than returned because the write path uses the
payload and the resume cursor comes from the ring that produced the
event; a caller that wanted both has the StreamEvent
it came from.
Sourcepub fn encode(&self) -> String
pub fn encode(&self) -> String
The bytes to write, without a trailing flush.
event: <name> and id: <n> first when present, then one data:
line per line of the payload, then the blank line that dispatches the
event. A payload with no newline in it is therefore exactly one data:
line, and one with k newlines is k + 1 — which is the claim
a_newline_in_the_payload_becomes_another_data_line
exists to hold.
Field order is not load-bearing: the standard dispatches on field
names, and this decoder does the same. event goes first because that
is the order the WHATWG examples use, and a reader that happened to care
about order would at least be agreeing with the examples.
Sourcepub fn decode(block: &str) -> Result<Self, SseError>
pub fn decode(block: &str) -> Result<Self, SseError>
Parses one frame’s block — its lines, without the trailing blank.
§Errors
SseError::NoData if the block carries no data: field,
SseError::MalformedId if an id: field is not a decimal event id,
and SseError::MalformedName if an event:
field is not a usable name.
Sourcepub fn dispatch(block: &str) -> Result<Option<Self>, SseError>
pub fn dispatch(block: &str) -> Result<Option<Self>, SseError>
The standard’s dispatch, which dispatches nothing for some blocks.
Returns None for a block that is empty or is entirely comments, and
that is the load-bearing difference from decode: a
keep-alive comment is a legal block that produces no event, and a stream
loop that treated it as one would hand its client an empty message
every heartbeat.
§Errors
SseError::MalformedId if an id: field is not a decimal event id.
A bad cursor is an error rather than a zero: EventId’s 0 is a real,
meaningful watermark meaning “before the first event”, and quietly
reading a corrupt id as that would tell a client it has seen everything
when it has seen nothing.
SseError::MalformedName if an event: field is empty or contains a
line terminator. An empty one is refused for the same reason a zero id
is not read as a watermark: event: with no value means the default
name, and treating it as a name of its own would make Some("") and
None two spellings of one dispatch.