Skip to main content

extract_response

Function extract_response 

Source
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:

  1. Errors take precedence. If any invocation matching call_id has 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.
  2. Otherwise, the first non-error invocation matching call_id is deserialized into T. 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 T may still parse. If a caller asks for extract_response::<EmailGetResponse>(&resp, "r1") but the matching invocation is actually a Mailbox/get response, serde_json::from_value will succeed on any structural overlap between the two shapes (both carry accountId, an ids/list field, etc.) and return a default-shaped EmailGetResponse. 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 in resp.method_responses order. 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 as T.

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.