pub fn extract_response<T: DeserializeOwned>(
resp: &JmapResponse,
call_id: &str,
) -> Result<T, ClientError>Expand description
Find the method response matching call_id in resp and deserialize its
arguments into T.
Returns ClientError::MethodNotFound if no invocation with the given
call_id exists. Returns ClientError::MethodError if any invocation
with the matching call_id is a JMAP "error" response (RFC 8620 §3.6.1).
§Multiple invocations sharing a call_id
Per RFC 8620 §3.2, a single method call may produce multiple invocations
in the response — for example, Foo/copy with onSuccessDestroyOriginal: true produces both a Foo/copy and an implicit Foo/set invocation,
both stamped with the same call_id (RFC 8620 §5.8 example, lines 3158–
3180). This function handles that case by:
- Errors take precedence. If any invocation matching
call_idhas method name"error", this function returns that error. A success response cannot mask a sibling error response with the same call_id — silently returning the success while the server reported failure would be data loss for the caller. - Otherwise, the first non-error invocation matching
call_idis deserialized intoT. In the §5.8 implicit-method case both invocations are successes; the first is the primary response and is what the caller wants.
§Contract: call_id is the only matcher (bd:JMAP-6r7c.23)
The method-name field of the matching invocation is not checked
against T. The function trusts the caller’s choice of call_id to
identify the invocation and trusts the server’s choice of method name
to be consistent with the request it was answering. Two consequences
callers should be aware of:
- Wrong
Tmay still parse. If a caller asks forextract_response::<EmailGetResponse>(&resp, "r1")but the matching invocation is actually aMailbox/getresponse,serde_json::from_valuewill succeed on any structural overlap between the two shapes (both carryaccountId, anids/listfield, etc.) and return a default-shapedEmailGetResponse. The function cannot detect this because it has no view of what method name the caller expected. - Server ordering is trusted. When multiple non-error invocations
share a
call_id, the function returns the first one inresp.method_responsesorder. RFC 8620 §5.8 implies the primary response is first, but the spec does not normatively require it; a non-conformant server that returns the implicit method first would silently get the wrong invocation deserialized asT.
Callers that need method-name verification or want to disambiguate
among multiple non-error matches should iterate
resp.method_responses directly. The field is public and the
jmap_types::Invocation type is (method, args, call_id).
§Panics
This function does not catch panics from T’s serde::Deserialize
implementation. If a custom T type’s deserialize impl panics — e.g.
because of an .unwrap() on a sub-field — the panic propagates through
extract_response to the caller’s await point. The standard derived
Deserialize impls in the workspace type crates (jmap-types,
jmap-mail-types, etc.) do not panic; this caveat only affects
hand-rolled Deserialize impls outside the workspace
(bd:JMAP-6r7c.44).
This function is pub so extension crates (jmap-chat-client,
jmap-mail-client) can use it to extract typed results from a
jmap_types::JmapResponse without depending on internal details.