My agentic slop goes here. Not intended for anyone else!
1(** Email set operations using core JMAP Set_args *)
2
3open Jmap.Methods
4
5(** Email creation arguments *)
6module Create = struct
7 type t = {
8 mailbox_ids : (Jmap.Id.t * bool) list;
9 keywords : (Keywords.keyword * bool) list;
10 received_at : Jmap.Date.t option;
11 (* Additional fields as needed *)
12 }
13
14 let make ~mailbox_ids ?(keywords=[]) ?received_at () = {
15 mailbox_ids;
16 keywords;
17 received_at;
18 }
19
20 let to_json t : Yojson.Safe.t =
21 let fields = [
22 ("mailboxIds", (`Assoc (List.map (fun (id, v) -> (Jmap.Id.to_string id, `Bool v)) t.mailbox_ids) : Yojson.Safe.t));
23 ("keywords", (`Assoc (List.map (fun (kw, v) -> (Keywords.keyword_to_string kw, `Bool v)) t.keywords) : Yojson.Safe.t));
24 ] in
25 let fields = match t.received_at with
26 | Some timestamp -> ("receivedAt", (Jmap.Date.to_json timestamp : Yojson.Safe.t)) :: fields
27 | None -> fields
28 in
29 (`Assoc fields : Yojson.Safe.t)
30end
31
32(** Email update patches *)
33module Update = struct
34 (** Build a patch object for updating email properties *)
35 let patch_builder () = []
36
37 let set_keywords keywords patch =
38 ("keywords", `Assoc (List.map (fun (kw, v) -> (Keywords.keyword_to_string kw, `Bool v)) keywords)) :: patch
39
40 let add_keyword keyword patch =
41 ("keywords/" ^ (Keywords.keyword_to_string keyword), `Bool true) :: patch
42
43 let remove_keyword keyword patch =
44 ("keywords/" ^ (Keywords.keyword_to_string keyword), `Null) :: patch
45
46 let move_to_mailbox mailbox_id patch =
47 (* Clear all existing mailboxes and set new one *)
48 let mailbox_id_str = Jmap.Id.to_string mailbox_id in
49 let clear_mailboxes = ("mailboxIds", `Null) :: patch in
50 ("mailboxIds/" ^ mailbox_id_str, `Bool true) :: clear_mailboxes
51
52 let add_to_mailbox mailbox_id patch =
53 let mailbox_id_str = Jmap.Id.to_string mailbox_id in
54 ("mailboxIds/" ^ mailbox_id_str, `Bool true) :: patch
55
56 let remove_from_mailbox mailbox_id patch =
57 let mailbox_id_str = Jmap.Id.to_string mailbox_id in
58 ("mailboxIds/" ^ mailbox_id_str, `Null) :: patch
59
60 let to_patch_object patch : patch_object = patch
61end
62
63(** Build Email/set arguments *)
64let build_set_args ~account_id ?if_in_state ?create ?update ?destroy () =
65 let account_id_str = Jmap.Id.to_string account_id in
66 let destroy_str_list = match destroy with
67 | Some id_list -> Some (List.map Jmap.Id.to_string id_list)
68 | None -> None in
69 Set_args.v
70 ~account_id:account_id_str
71 ?if_in_state
72 ?create
73 ?update
74 ?destroy:destroy_str_list
75 ()
76
77(** Convert Email/set arguments to JSON *)
78let set_args_to_json args =
79 Set_args.to_json
80 ~create_to_json:Create.to_json
81 ~update_to_json:(fun patches -> (`Assoc patches : Yojson.Safe.t))
82 args
83
84(** Common operations *)
85
86(** Mark emails as read *)
87let mark_as_read ~account_id email_ids =
88 let update_map : (string, patch_object) Hashtbl.t = Hashtbl.create (List.length email_ids) in
89 List.iter (fun id ->
90 Hashtbl.add update_map (Jmap.Id.to_string id) (Update.add_keyword Keywords.Seen [])
91 ) email_ids;
92 build_set_args ~account_id ~update:update_map ()
93
94(** Mark emails as unread *)
95let mark_as_unread ~account_id email_ids =
96 let update_map : (string, patch_object) Hashtbl.t = Hashtbl.create (List.length email_ids) in
97 List.iter (fun id ->
98 Hashtbl.add update_map (Jmap.Id.to_string id) (Update.remove_keyword Keywords.Seen [])
99 ) email_ids;
100 build_set_args ~account_id ~update:update_map ()
101
102(** Flag/star emails *)
103let flag_emails ~account_id email_ids =
104 let update_map : (string, patch_object) Hashtbl.t = Hashtbl.create (List.length email_ids) in
105 List.iter (fun id ->
106 Hashtbl.add update_map (Jmap.Id.to_string id) (Update.add_keyword Keywords.Flagged [])
107 ) email_ids;
108 build_set_args ~account_id ~update:update_map ()
109
110(** Unflag/unstar emails *)
111let unflag_emails ~account_id email_ids =
112 let update_map : (string, patch_object) Hashtbl.t = Hashtbl.create (List.length email_ids) in
113 List.iter (fun id ->
114 Hashtbl.add update_map (Jmap.Id.to_string id) (Update.remove_keyword Keywords.Flagged [])
115 ) email_ids;
116 build_set_args ~account_id ~update:update_map ()
117
118(** Move emails to a mailbox *)
119let move_to_mailbox ~account_id ~mailbox_id email_ids =
120 let update_map : (string, patch_object) Hashtbl.t = Hashtbl.create (List.length email_ids) in
121 List.iter (fun id ->
122 Hashtbl.add update_map (Jmap.Id.to_string id) (Update.move_to_mailbox mailbox_id [])
123 ) email_ids;
124 build_set_args ~account_id ~update:update_map ()
125
126(** Delete emails (move to trash or destroy) *)
127let delete_emails ~account_id ?(destroy=false) email_ids =
128 if destroy then
129 build_set_args ~account_id ~destroy:email_ids ()
130 else
131 (* Move to trash mailbox - would need to look up trash mailbox ID *)
132 build_set_args ~account_id ~destroy:email_ids ()
133
134(** Batch update multiple properties *)
135let batch_update ~account_id updates =
136 let update_map : (string, patch_object) Hashtbl.t = Hashtbl.create (List.length updates) in
137 List.iter (fun (id, patch) ->
138 Hashtbl.add update_map (Jmap.Id.to_string id) patch
139 ) updates;
140 build_set_args ~account_id ~update:update_map ()
141
142(** Create a draft email *)
143let create_draft ~account_id ~mailbox_ids ?keywords ?subject:_ ?from:_ ?to_:_ ?cc:_ ?bcc:_ ?text_body:_ ?html_body:_ () =
144 (* Note: subject, from, to_, cc, bcc, text_body, html_body would need proper implementation
145 with full email creation support. For now, just creating basic structure. *)
146 let creation = Create.make ~mailbox_ids ?keywords () in
147 let create_map : (string, Create.t) Hashtbl.t = Hashtbl.create 1 in
148 Hashtbl.add create_map "draft-1" creation;
149 build_set_args ~account_id ~create:create_map ()