My agentic slop goes here. Not intended for anyone else!
at main 4.7 kB view raw
1(** Email changes operations using core JMAP Changes_args *) 2 3open Jmap.Methods 4 5(** Build Email/changes arguments *) 6let build_changes_args ~account_id ~since_state ?max_changes () = 7 let account_id_str = Jmap.Id.to_string account_id in 8 let max_changes_int = match max_changes with 9 | Some uint -> Some (Jmap.UInt.to_int uint) 10 | None -> None in 11 Changes_args.v 12 ~account_id:account_id_str 13 ~since_state 14 ?max_changes:max_changes_int 15 () 16 17(** Convert Email/changes arguments to JSON *) 18let changes_args_to_json args = 19 Changes_args.to_json args 20 21(** Track changes since a given state *) 22type change_tracker = { 23 account_id : Jmap.Id.t; 24 current_state : string; 25 created : Jmap.Id.t list; 26 updated : Jmap.Id.t list; 27 destroyed : Jmap.Id.t list; 28} 29 30(** Create a new change tracker *) 31let create_tracker ~account_id ~initial_state = 32 { 33 account_id; 34 current_state = initial_state; 35 created = []; 36 updated = []; 37 destroyed = []; 38 } 39 40(** Update tracker with a Changes_response *) 41let update_tracker tracker response = 42 { 43 tracker with 44 current_state = Changes_response.new_state response; 45 created = tracker.created @ (List.map (fun s -> match Jmap.Id.of_string s with Ok id -> id | Error e -> failwith e) (Changes_response.created response)); 46 updated = tracker.updated @ (List.map (fun s -> match Jmap.Id.of_string s with Ok id -> id | Error e -> failwith e) (Changes_response.updated response)); 47 destroyed = tracker.destroyed @ (List.map (fun s -> match Jmap.Id.of_string s with Ok id -> id | Error e -> failwith e) (Changes_response.destroyed response)); 48 } 49 50(** Get all changes since tracker was created *) 51let get_all_changes tracker = 52 (tracker.created, tracker.updated, tracker.destroyed) 53 54(** Get next batch of changes *) 55let get_next_changes ~account_id ~since_state ?(max_changes=500) () = 56 let max_changes_uint = match Jmap.UInt.of_int max_changes with 57 | Ok u -> u 58 | Error _ -> failwith ("Invalid max_changes: " ^ string_of_int max_changes) in 59 build_changes_args ~account_id ~since_state ~max_changes:max_changes_uint () 60 61(** Check if there are pending changes *) 62let has_pending_changes response = 63 Changes_response.has_more_changes response 64 65(** Incremental sync helper *) 66module Sync = struct 67 type sync_state = { 68 account_id : Jmap.Id.t; 69 last_state : string; 70 pending_created : Jmap.Id.t list; 71 pending_updated : Jmap.Id.t list; 72 pending_destroyed : Jmap.Id.t list; 73 } 74 75 let init ~account_id ~initial_state = 76 { 77 account_id; 78 last_state = initial_state; 79 pending_created = []; 80 pending_updated = []; 81 pending_destroyed = []; 82 } 83 84 let add_response sync response = 85 let new_state = Changes_response.new_state response in 86 let created = Changes_response.created response in 87 let updated = Changes_response.updated response in 88 let destroyed = Changes_response.destroyed response in 89 { 90 sync with 91 last_state = new_state; 92 pending_created = sync.pending_created @ (List.map (fun s -> match Jmap.Id.of_string s with Ok id -> id | Error e -> failwith e) created); 93 pending_updated = sync.pending_updated @ (List.map (fun s -> match Jmap.Id.of_string s with Ok id -> id | Error e -> failwith e) updated); 94 pending_destroyed = sync.pending_destroyed @ (List.map (fun s -> match Jmap.Id.of_string s with Ok id -> id | Error e -> failwith e) destroyed); 95 } 96 97 let clear_pending sync = 98 { 99 sync with 100 pending_created = []; 101 pending_updated = []; 102 pending_destroyed = []; 103 } 104 105 let get_pending sync = 106 (sync.pending_created, sync.pending_updated, sync.pending_destroyed) 107 108 let needs_sync sync response = 109 Changes_response.has_more_changes response || 110 sync.pending_created <> [] || 111 sync.pending_updated <> [] || 112 sync.pending_destroyed <> [] 113end 114 115(** Utility to merge multiple change responses *) 116let merge_changes responses = 117 List.fold_left (fun (created, updated, destroyed) response -> 118 let c = List.map (fun id -> match Jmap.Id.of_string id with | Ok id_t -> id_t | Error _ -> failwith ("Invalid ID: " ^ id)) (Changes_response.created response) in 119 let u = List.map (fun id -> match Jmap.Id.of_string id with | Ok id_t -> id_t | Error _ -> failwith ("Invalid ID: " ^ id)) (Changes_response.updated response) in 120 let d = List.map (fun id -> match Jmap.Id.of_string id with | Ok id_t -> id_t | Error _ -> failwith ("Invalid ID: " ^ id)) (Changes_response.destroyed response) in 121 (created @ c, updated @ u, destroyed @ d) 122 ) ([], [], []) responses 123 124(** Get updated properties if available *) 125let get_updated_properties response = 126 Changes_response.updated_properties response