(** JMAP Response Object A Response object is returned from the JMAP API endpoint in response to a Request. It contains method responses and the current session state. Reference: RFC 8620 Section 3.4 Test files: - test/data/core/response_echo.json - test/data/core/response_get.json - All response_*.json files *) (** Main response type *) type t = { method_responses : Jmap_invocation.response_list; created_ids : (Jmap_id.t * Jmap_id.t) list option; session_state : string; } (** Accessors *) let method_responses t = t.method_responses let created_ids t = t.created_ids let session_state t = t.session_state (** Create a response *) let make ?(created_ids=None) ~method_responses ~session_state () = { method_responses; created_ids; session_state } (** Parser submodule *) module Parser = struct (** Parse response from JSON value. Test files: test/data/core/response_*.json *) let of_json json = match json with | `O fields -> let get_field name = match List.assoc_opt name fields with | Some v -> v | None -> raise (Jmap_error.Parse_error (Printf.sprintf "Missing field: %s" name)) in (* Parse methodResponses - similar to parsing request methodCalls *) let method_responses = match get_field "methodResponses" with | `A responses -> List.map (fun resp_json -> (* Each response is ["method", {...}, "callId"] *) (* For now, just parse as generic invocations *) match resp_json with | `A [(`String method_name); response; (`String call_id)] -> (* Parse as response invocation, storing raw JSON *) Jmap_invocation.PackedResponse (Jmap_invocation.ResponseInvocation { method_name; response; call_id; witness = Jmap_invocation.Echo; }) | _ -> raise (Jmap_error.Parse_error "Invalid method response format") ) responses | _ -> raise (Jmap_error.Parse_error "methodResponses must be an array") in (* Parse createdIds (optional) *) let created_ids = match List.assoc_opt "createdIds" fields with | Some (`O ids) -> Some (List.map (fun (k, v) -> match v with | `String id -> (Jmap_id.of_string k, Jmap_id.of_string id) | _ -> raise (Jmap_error.Parse_error "createdIds values must be strings") ) ids) | Some _ -> raise (Jmap_error.Parse_error "createdIds must be an object") | None -> None in (* Parse sessionState *) let session_state = match get_field "sessionState" with | `String s -> s | _ -> raise (Jmap_error.Parse_error "sessionState must be a string") in { method_responses; created_ids; session_state } | _ -> raise (Jmap_error.Parse_error "Response must be a JSON object") (** Parse response from JSON string *) let of_string s = try of_json (Ezjsonm.from_string s) with | Ezjsonm.Parse_error (_, msg) -> raise (Jmap_error.Parse_error ("Invalid JSON: " ^ msg)) (** Parse response from input channel *) let of_channel ic = try of_json (Ezjsonm.from_channel ic) with | Ezjsonm.Parse_error (_, msg) -> raise (Jmap_error.Parse_error ("Invalid JSON: " ^ msg)) end (** Serialization *) let to_json _t = (* TODO: Implement JSON serialization *) raise (Jmap_error.Parse_error "Response.to_json not yet implemented")