Skip to main content

Module reply

Module reply 

Source
Expand description

The reply document, assembled from a dispatched result and a request id.

§Why this is not a codec

Codec lifts and lowers a ToolResult and nothing else, so nothing in the crate yet turns a Dispatcher result plus a request id into the document a peer reads. Rather than reach past it, this module writes that document and says so where a reader will find it. A request-side codec landing later replaces these four functions and nothing else, because the serve loop knows no method names and no revision: it hands a result and an id to this module and writes the bytes it gets back.

§Nothing is spliced, and that is the point

The obvious implementation is string concatenation — take JsonObject::as_str and write it between two members. It is faster and it is wrong, and the reason is worth stating because the mistake is invisible in review and on the happy path.

JsonObject::new validates its text and then stores it verbatim. JSON permits whitespace between tokens, so JsonObject::new("{\n\"a\":1\n}") succeeds and as_str hands back two raw newlines. Splicing that into a newline-delimited frame splits one reply into two lines, and a peer reading them sees two malformed frames instead of an answer. No test of the handler would notice, because the corruption happens afterwards, in the transport.

So every document here is built as a serde_json::Value and serialised once. Value’s writer escapes control characters rather than passing them through, which makes a raw newline in a reply structurally impossible instead of merely unlikely — and it is one code path, so there is no second serialiser to drift from the first.

§The id is re-escaped, not debug-printed

WireId::to_json is the right answer for a caller splicing an id, and format!("{id:?}") is the wrong one in a way this crate has already paid for: Debug for a string is escape_debug, which writes DEL as \u{7f}. That is Rust syntax and not JSON, so the peer would see a parse error rather than its own id. Both builders below hand the id to serde_json as a Value, which escapes it the same way to_json does, for the same reason.

§What a parse failure is answered with

A frame that is not JSON has no id — there was never one to read — so unidentified_error_reply writes "id": null, which is what the specification requires and the only honest answer available. A caller that invents an id for a frame it could not parse is telling the peer something about a message the peer never successfully sent.

Enums§

ReplyError
Why a reply could not be assembled.

Functions§

error_reply
A failed reply to the request with this id.
error_reply_with_data
A failed reply carrying the structured data some codes require.
result_reply
A successful reply to the request with this id.
unidentified_error_reply
A failed reply to a frame that never had an id to answer.