(** Implementation of JMAP SearchSnippet objects (RFC 8621 Section 5) *) type t = { email_id : Jmap.Id.t; subject : string option; preview : string option; } (** {1 SearchSnippet Construction} *) let create ~email_id ?subject ?preview () = { email_id; subject; preview; } (** {1 Field Access} *) let email_id t = t.email_id let subject t = t.subject let preview t = t.preview (** {1 JSON Serialization} *) let to_json t = let json_fields = [ ("emailId", `String (Jmap.Id.to_string t.email_id)); ] in let json_fields = match t.subject with | Some s -> ("subject", `String s) :: json_fields | None -> ("subject", `Null) :: json_fields in let json_fields = match t.preview with | Some p -> ("preview", `String p) :: json_fields | None -> ("preview", `Null) :: json_fields in `Assoc (List.rev json_fields) let of_json json = try let open Yojson.Safe.Util in let email_id_str = json |> member "emailId" |> to_string in let email_id = match Jmap.Id.of_string email_id_str with | Ok id -> id | Error _ -> failwith ("Invalid emailId: " ^ email_id_str) in let subject = json |> member "subject" |> to_string_option in let preview = json |> member "preview" |> to_string_option in Ok (create ~email_id ?subject ?preview ()) with | exn -> Error ("Failed to parse SearchSnippet: " ^ Printexc.to_string exn) (** {1 SearchSnippet/get Method Support} *) module Get_args = struct type t = { account_id : string; filter : Yojson.Safe.t; (* Use raw JSON for now since Filter module doesn't have of_json *) email_ids : Jmap.Id.t list; } let create ~account_id ~filter ~email_ids () = { account_id; filter; email_ids; } let account_id t = t.account_id let filter t = t.filter let email_ids t = t.email_ids let to_json t = `Assoc [ ("accountId", `String t.account_id); ("filter", t.filter); ("emailIds", `List (List.map (fun id -> `String (Jmap.Id.to_string id)) t.email_ids)); ] let of_json json = try let open Yojson.Safe.Util in let account_id = json |> member "accountId" |> to_string in let filter = json |> member "filter" in let email_ids_json = json |> member "emailIds" |> to_list in let email_ids = List.map (fun id_json -> let id_str = to_string id_json in match Jmap.Id.of_string id_str with | Ok id -> id | Error _ -> failwith ("Invalid email ID: " ^ id_str) ) email_ids_json in Ok (create ~account_id ~filter ~email_ids ()) with | exn -> Error ("Failed to parse SearchSnippet/get args: " ^ Printexc.to_string exn) end module Get_response = struct type snippet = t (* Reference to the outer SearchSnippet.t *) type response = { account_id : string; list : snippet list; not_found : Jmap.Id.t list; } let create ~account_id ~list ?(not_found=[]) () = { account_id; list; not_found; } let account_id t = t.account_id let list t = t.list let not_found t = t.not_found let to_json t = `Assoc [ ("accountId", `String t.account_id); ("list", `List (List.map to_json t.list)); ("notFound", match t.not_found with | [] -> `Null | ids -> `List (List.map (fun id -> `String (Jmap.Id.to_string id)) ids)); ] let of_json json = try let open Yojson.Safe.Util in let account_id = json |> member "accountId" |> to_string in let list_json = json |> member "list" |> to_list in let list = List.map (fun snippet_json -> match of_json snippet_json with | Ok snippet -> snippet | Error err -> failwith err ) list_json in let not_found = match json |> member "notFound" with | `Null -> [] | `List ids -> List.map (fun id_json -> let id_str = to_string id_json in match Jmap.Id.of_string id_str with | Ok id -> id | Error _ -> failwith ("Invalid not found ID: " ^ id_str) ) ids | _ -> failwith "notFound must be null or array" in Ok (create ~account_id ~list ~not_found ()) with | exn -> Error ("Failed to parse SearchSnippet/get response: " ^ Printexc.to_string exn) end