+8
-8
bin/fastmail_list.ml
+8
-8
bin/fastmail_list.ml
···
18
18
19
19
open Lwt.Syntax
20
20
open Jmap
21
-
open Jmap_mail
21
+
open Jmap.Mail
22
22
open Cmdliner
23
-
module Mail = Jmap_mail.Types
23
+
module Mail = Jmap.Proto.Types
24
24
25
25
(** Prints the email details *)
26
26
let print_email ~show_labels (email : Mail.email) =
···
42
42
(* Format labels/keywords if requested *)
43
43
let labels_str =
44
44
if show_labels then
45
-
let formatted = Jmap_mail.Types.format_email_keywords email.keywords in
45
+
let formatted = Jmap.Proto.Types.format_email_keywords email.keywords in
46
46
if formatted <> "" then
47
47
" [" ^ formatted ^ "]"
48
48
else
···
73
73
Printf.printf "=====================\n";
74
74
75
75
(* Step 1: Get all mailboxes *)
76
-
let* mailboxes_result = Jmap_mail.get_mailboxes conn ~account_id in
76
+
let* mailboxes_result = Jmap.Proto.get_mailboxes conn ~account_id in
77
77
match mailboxes_result with
78
78
| Error err ->
79
79
Printf.printf "Error getting mailboxes: %s\n" (Api.string_of_error err);
···
90
90
Printf.printf "Using mailbox: %s\n" first_mailbox.Mail.name;
91
91
92
92
(* Step 3: Get emails from the selected mailbox *)
93
-
let* emails_result = Jmap_mail.get_messages_in_mailbox
93
+
let* emails_result = Jmap.Proto.get_messages_in_mailbox
94
94
conn
95
95
~account_id
96
96
~mailbox_id:first_mailbox.Mail.id
···
108
108
(List.length emails);
109
109
110
110
(* Display some basic information about the emails *)
111
-
List.iteri (fun i (email:Jmap_mail.Types.email) ->
111
+
List.iteri (fun i (email:Jmap.Proto.Types.email) ->
112
112
let subject = Option.value ~default:"<no subject>" email.Mail.subject in
113
113
Printf.printf " %d. %s\n" (i + 1) subject
114
114
) emails;
···
159
159
| Ok conn ->
160
160
(* Get the primary account ID *)
161
161
let primary_account_id =
162
-
let mail_capability = Jmap_mail.Capability.to_string Jmap_mail.Capability.Mail in
162
+
let mail_capability = Jmap.Proto.Capability.to_string Jmap.Proto.Capability.Mail in
163
163
match List.assoc_opt mail_capability conn.session.primary_accounts with
164
164
| Some id -> id
165
165
| None ->
···
220
220
if sender_filter <> "" then begin
221
221
Printf.printf "Filtering by sender: %s\n" sender_filter;
222
222
List.filter (fun email ->
223
-
Jmap_mail.email_matches_sender email sender_filter
223
+
Jmap.Proto.email_matches_sender email sender_filter
224
224
) filtered_by_unread
225
225
end else
226
226
filtered_by_unread
+7
-7
bin/fastmail_send.ml
+7
-7
bin/fastmail_send.ml
···
52
52
(* Initialize JMAP connection *)
53
53
let fastmail_uri = "https://api.fastmail.com/jmap/session" in
54
54
Lwt_main.run begin
55
-
let* conn_result = Jmap_mail.login_with_token ~uri:fastmail_uri ~api_token:token in
55
+
let* conn_result = Jmap.Proto.login_with_token ~uri:fastmail_uri ~api_token:token in
56
56
match conn_result with
57
57
| Error err ->
58
58
let msg = Jmap.Api.string_of_error err in
···
78
78
| Some email -> Lwt.return_ok email
79
79
| None ->
80
80
(* Get first available identity *)
81
-
let* identities_result = Jmap_mail.get_identities conn ~account_id in
81
+
let* identities_result = Jmap.Proto.get_identities conn ~account_id in
82
82
match identities_result with
83
83
| Ok [] ->
84
84
log_error "No identities found for account";
···
99
99
(String.concat ", " to_addresses);
100
100
101
101
let* submission_result =
102
-
Jmap_mail.create_and_submit_email
102
+
Jmap.Proto.create_and_submit_email
103
103
conn
104
104
~account_id
105
105
~from:from_email
···
118
118
log_success "Email sent successfully (Submission ID: %s)" submission_id;
119
119
(* Wait briefly then check submission status *)
120
120
let* () = Lwt_unix.sleep 1.0 in
121
-
let* status_result = Jmap_mail.get_submission_status
121
+
let* status_result = Jmap.Proto.get_submission_status
122
122
conn
123
123
~account_id
124
124
~submission_id
···
126
126
127
127
(match status_result with
128
128
| Ok status ->
129
-
let status_text = match status.Jmap_mail.Types.undo_status with
129
+
let status_text = match status.Jmap.Proto.Types.undo_status with
130
130
| Some `pending -> "Pending"
131
131
| Some `final -> "Final (delivered)"
132
132
| Some `canceled -> "Canceled"
···
134
134
in
135
135
log_info "Submission status: %s" status_text;
136
136
137
-
(match status.Jmap_mail.Types.delivery_status with
137
+
(match status.Jmap.Proto.Types.delivery_status with
138
138
| Some statuses ->
139
139
List.iter (fun (email, status) ->
140
-
let delivery = match status.Jmap_mail.Types.delivered with
140
+
let delivery = match status.Jmap.Proto.Types.delivered with
141
141
| Some "yes" -> "Delivered"
142
142
| Some "no" -> "Failed"
143
143
| Some "queued" -> "Queued"
+48
-48
bin/jmap.ml
+48
-48
bin/jmap.ml
···
17
17
if String.length s <= max_len then s
18
18
else String.sub s 0 (max_len - 3) ^ "..."
19
19
20
-
let format_email_address (addr : Jmap_mail.Email_address.t) =
20
+
let format_email_address (addr : Jmap.Proto.Email_address.t) =
21
21
match addr.name with
22
22
| Some name -> Printf.sprintf "%s <%s>" name addr.email
23
23
| None -> addr.email
···
51
51
) session.capabilities;
52
52
Fmt.pr "@, %a@," Fmt.(styled `Bold string) "Accounts:";
53
53
List.iter (fun (id, acct) ->
54
-
let acct : Jmap_proto.Session.Account.t = acct in
54
+
let acct : Jmap.Proto.Session.Account.t = acct in
55
55
Fmt.pr " %a: %s (personal=%b, read_only=%b)@,"
56
-
Fmt.(styled `Cyan string) (Jmap_proto.Id.to_string id)
56
+
Fmt.(styled `Cyan string) (Jmap.Proto.Id.to_string id)
57
57
acct.name acct.is_personal acct.is_read_only
58
58
) session.accounts;
59
59
Fmt.pr "@, %a@," Fmt.(styled `Bold string) "Primary Accounts:";
60
60
List.iter (fun (cap, id) ->
61
-
Fmt.pr " %s: %s@," cap (Jmap_proto.Id.to_string id)
61
+
Fmt.pr " %s: %s@," cap (Jmap.Proto.Id.to_string id)
62
62
) session.primary_accounts;
63
63
Fmt.pr "@]@."
64
64
in
···
75
75
let client = Jmap_eio.Cli.create_client ~sw env cfg in
76
76
let account_id = Jmap_eio.Cli.get_account_id cfg client in
77
77
78
-
Jmap_eio.Cli.debug cfg "Fetching mailboxes for account %s" (Jmap_proto.Id.to_string account_id);
78
+
Jmap_eio.Cli.debug cfg "Fetching mailboxes for account %s" (Jmap.Proto.Id.to_string account_id);
79
79
80
80
let req = Jmap_eio.Client.Build.(
81
81
make_request
82
-
~capabilities:[Jmap_proto.Capability.core; Jmap_proto.Capability.mail]
82
+
~capabilities:[Jmap.Proto.Capability.core; Jmap.Proto.Capability.mail]
83
83
[mailbox_get ~call_id:"m1" ~account_id ()]
84
84
) in
85
85
···
97
97
Fmt.(styled `Bold string) "Mailboxes"
98
98
result.state;
99
99
(* Sort by sort_order then name *)
100
-
let sorted = List.sort (fun (a : Jmap_mail.Mailbox.t) (b : Jmap_mail.Mailbox.t) ->
100
+
let sorted = List.sort (fun (a : Jmap.Proto.Mailbox.t) (b : Jmap.Proto.Mailbox.t) ->
101
101
let cmp = Int64.compare a.sort_order b.sort_order in
102
102
if cmp <> 0 then cmp else String.compare a.name b.name
103
103
) result.list in
104
-
List.iter (fun (mbox : Jmap_mail.Mailbox.t) ->
104
+
List.iter (fun (mbox : Jmap.Proto.Mailbox.t) ->
105
105
let role_str = match mbox.role with
106
-
| Some role -> Printf.sprintf " [%s]" (Jmap_mail.Mailbox.role_to_string role)
106
+
| Some role -> Printf.sprintf " [%s]" (Jmap.Proto.Mailbox.role_to_string role)
107
107
| None -> ""
108
108
in
109
109
Fmt.pr " %a %s%a (%Ld total, %Ld unread)@,"
110
-
Fmt.(styled `Cyan string) (Jmap_proto.Id.to_string mbox.id)
110
+
Fmt.(styled `Cyan string) (Jmap.Proto.Id.to_string mbox.id)
111
111
mbox.name
112
112
Fmt.(styled `Yellow string) role_str
113
113
mbox.total_emails mbox.unread_emails
···
140
140
(* Build filter if mailbox specified *)
141
141
let filter = match mailbox_id_str with
142
142
| Some id_str ->
143
-
let mailbox_id = Jmap_proto.Id.of_string_exn id_str in
144
-
let cond : Jmap_mail.Email.Filter_condition.t = {
143
+
let mailbox_id = Jmap.Proto.Id.of_string_exn id_str in
144
+
let cond : Jmap.Proto.Email.Filter_condition.t = {
145
145
in_mailbox = Some mailbox_id;
146
146
in_mailbox_other_than = None;
147
147
before = None; after = None;
···
155
155
cc = None; bcc = None; subject = None;
156
156
body = None; header = None;
157
157
} in
158
-
Some (Jmap_proto.Filter.Condition cond)
158
+
Some (Jmap.Proto.Filter.Condition cond)
159
159
| None -> None
160
160
in
161
161
162
-
let sort = [Jmap_proto.Filter.comparator ~is_ascending:false "receivedAt"] in
162
+
let sort = [Jmap.Proto.Filter.comparator ~is_ascending:false "receivedAt"] in
163
163
let query_inv = Jmap_eio.Client.Build.email_query
164
164
~call_id:"q1"
165
165
~account_id
···
171
171
172
172
let req = Jmap_eio.Client.Build.(
173
173
make_request
174
-
~capabilities:[Jmap_proto.Capability.core; Jmap_proto.Capability.mail]
174
+
~capabilities:[Jmap.Proto.Capability.core; Jmap.Proto.Capability.mail]
175
175
[query_inv]
176
176
) in
177
177
···
202
202
in
203
203
let req2 = Jmap_eio.Client.Build.(
204
204
make_request
205
-
~capabilities:[Jmap_proto.Capability.core; Jmap_proto.Capability.mail]
205
+
~capabilities:[Jmap.Proto.Capability.core; Jmap.Proto.Capability.mail]
206
206
[get_inv]
207
207
) in
208
208
···
222
222
(match query_result.total with
223
223
| Some n -> Int64.to_string n
224
224
| None -> "?");
225
-
List.iter (fun (email : Jmap_mail.Email.t) ->
225
+
List.iter (fun (email : Jmap.Proto.Email.t) ->
226
226
let from_str = match email.from with
227
227
| Some addrs -> format_email_addresses addrs
228
228
| None -> "(unknown)"
···
231
231
let flags = format_keywords email.keywords in
232
232
let flag_str = if flags = "" then "" else " [" ^ flags ^ "]" in
233
233
Fmt.pr " %a %s@,"
234
-
Fmt.(styled `Cyan string) (Jmap_proto.Id.to_string email.id)
234
+
Fmt.(styled `Cyan string) (Jmap.Proto.Id.to_string email.id)
235
235
(ptime_to_string email.received_at);
236
236
Fmt.pr " From: %s@," (truncate_string 60 from_str);
237
237
Fmt.pr " Subject: %a%s@,"
···
267
267
Jmap_eio.Cli.debug cfg "Searching for: %s" query;
268
268
269
269
(* Build text filter *)
270
-
let cond : Jmap_mail.Email.Filter_condition.t = {
270
+
let cond : Jmap.Proto.Email.Filter_condition.t = {
271
271
in_mailbox = None; in_mailbox_other_than = None;
272
272
before = None; after = None;
273
273
min_size = None; max_size = None;
···
281
281
cc = None; bcc = None; subject = None;
282
282
body = None; header = None;
283
283
} in
284
-
let filter = Jmap_proto.Filter.Condition cond in
284
+
let filter = Jmap.Proto.Filter.Condition cond in
285
285
286
-
let sort = [Jmap_proto.Filter.comparator ~is_ascending:false "receivedAt"] in
286
+
let sort = [Jmap.Proto.Filter.comparator ~is_ascending:false "receivedAt"] in
287
287
let query_inv = Jmap_eio.Client.Build.email_query
288
288
~call_id:"q1"
289
289
~account_id
···
295
295
296
296
let req = Jmap_eio.Client.Build.(
297
297
make_request
298
-
~capabilities:[Jmap_proto.Capability.core; Jmap_proto.Capability.mail]
298
+
~capabilities:[Jmap.Proto.Capability.core; Jmap.Proto.Capability.mail]
299
299
[query_inv]
300
300
) in
301
301
···
325
325
in
326
326
let req2 = Jmap_eio.Client.Build.(
327
327
make_request
328
-
~capabilities:[Jmap_proto.Capability.core; Jmap_proto.Capability.mail]
328
+
~capabilities:[Jmap.Proto.Capability.core; Jmap.Proto.Capability.mail]
329
329
[get_inv]
330
330
) in
331
331
···
343
343
Fmt.(styled `Bold string) "Search results"
344
344
query
345
345
(List.length get_result.list);
346
-
List.iter (fun (email : Jmap_mail.Email.t) ->
346
+
List.iter (fun (email : Jmap.Proto.Email.t) ->
347
347
let from_str = match email.from with
348
348
| Some addrs -> format_email_addresses addrs
349
349
| None -> "(unknown)"
350
350
in
351
351
let subject = Option.value email.subject ~default:"(no subject)" in
352
352
Fmt.pr " %a %s@,"
353
-
Fmt.(styled `Cyan string) (Jmap_proto.Id.to_string email.id)
353
+
Fmt.(styled `Cyan string) (Jmap.Proto.Id.to_string email.id)
354
354
(ptime_to_string email.received_at);
355
355
Fmt.pr " From: %s@," (truncate_string 60 from_str);
356
356
Fmt.pr " Subject: %a@,"
···
387
387
Jmap_eio.Cli.debug cfg "Fetching %d most recent emails" limit;
388
388
389
389
(* Query for recent emails *)
390
-
let sort = [Jmap_proto.Filter.comparator ~is_ascending:false "receivedAt"] in
390
+
let sort = [Jmap.Proto.Filter.comparator ~is_ascending:false "receivedAt"] in
391
391
let query_inv = Jmap_eio.Client.Build.email_query
392
392
~call_id:"q1"
393
393
~account_id
···
398
398
399
399
let req = Jmap_eio.Client.Build.(
400
400
make_request
401
-
~capabilities:[Jmap_proto.Capability.core; Jmap_proto.Capability.mail]
401
+
~capabilities:[Jmap.Proto.Capability.core; Jmap.Proto.Capability.mail]
402
402
[query_inv]
403
403
) in
404
404
···
432
432
in
433
433
let req2 = Jmap_eio.Client.Build.(
434
434
make_request
435
-
~capabilities:[Jmap_proto.Capability.core; Jmap_proto.Capability.mail]
435
+
~capabilities:[Jmap.Proto.Capability.core; Jmap.Proto.Capability.mail]
436
436
[get_inv]
437
437
) in
438
438
···
453
453
(* Output based on format *)
454
454
match format with
455
455
| `Compact ->
456
-
List.iter (fun (email : Jmap_mail.Email.t) ->
456
+
List.iter (fun (email : Jmap.Proto.Email.t) ->
457
457
let from_str = match email.from with
458
458
| Some (addr :: _) ->
459
459
Option.value addr.name ~default:addr.email
···
462
462
let subject = Option.value email.subject ~default:"(no subject)" in
463
463
let flags = format_keywords email.keywords in
464
464
Printf.printf "%s\t%s\t%s\t%s\t%s\n"
465
-
(Jmap_proto.Id.to_string email.id)
465
+
(Jmap.Proto.Id.to_string email.id)
466
466
(ptime_to_string email.received_at)
467
467
(truncate_string 20 from_str)
468
468
(truncate_string 50 subject)
···
478
478
Fmt.pr "%-12s %-19s %-20s %-40s %s@,"
479
479
"ID" "Date" "From" "Subject" "Flags";
480
480
Fmt.pr "%s@," (String.make 110 '-');
481
-
List.iter (fun (email : Jmap_mail.Email.t) ->
481
+
List.iter (fun (email : Jmap.Proto.Email.t) ->
482
482
let from_str = match email.from with
483
483
| Some (addr :: _) ->
484
484
Option.value addr.name ~default:addr.email
···
487
487
let subject = Option.value email.subject ~default:"(no subject)" in
488
488
let flags = format_keywords email.keywords in
489
489
let id_short =
490
-
let id = Jmap_proto.Id.to_string email.id in
490
+
let id = Jmap.Proto.Id.to_string email.id in
491
491
if String.length id > 12 then String.sub id 0 12 else id
492
492
in
493
493
Fmt.pr "%-12s %s %-20s %-40s %s@,"
···
503
503
Fmt.pr "@[<v>%a (%d emails)@,@,"
504
504
Fmt.(styled `Bold string) "Recent Emails"
505
505
(List.length get_result.list);
506
-
List.iteri (fun i (email : Jmap_mail.Email.t) ->
506
+
List.iteri (fun i (email : Jmap.Proto.Email.t) ->
507
507
let from_str = match email.from with
508
508
| Some addrs -> format_email_addresses addrs
509
509
| None -> "(unknown)"
···
524
524
Fmt.(styled `Bold string) "---"
525
525
(i + 1) (List.length get_result.list);
526
526
Fmt.pr "ID: %a@,"
527
-
Fmt.(styled `Cyan string) (Jmap_proto.Id.to_string email.id);
528
-
Fmt.pr "Thread: %s@," (Jmap_proto.Id.to_string email.thread_id);
527
+
Fmt.(styled `Cyan string) (Jmap.Proto.Id.to_string email.id);
528
+
Fmt.pr "Thread: %s@," (Jmap.Proto.Id.to_string email.thread_id);
529
529
Fmt.pr "Date: %s@," (ptime_to_string email.received_at);
530
530
Fmt.pr "From: %s@," from_str;
531
531
if to_str <> "" then Fmt.pr "To: %s@," to_str;
···
557
557
let client = Jmap_eio.Cli.create_client ~sw env cfg in
558
558
let account_id = Jmap_eio.Cli.get_account_id cfg client in
559
559
560
-
let email_id = Jmap_proto.Id.of_string_exn email_id_str in
560
+
let email_id = Jmap.Proto.Id.of_string_exn email_id_str in
561
561
562
562
(* First get the email to find its thread ID - include required properties *)
563
563
let get_inv = Jmap_eio.Client.Build.email_get
···
569
569
in
570
570
let req = Jmap_eio.Client.Build.(
571
571
make_request
572
-
~capabilities:[Jmap_proto.Capability.core; Jmap_proto.Capability.mail]
572
+
~capabilities:[Jmap.Proto.Capability.core; Jmap.Proto.Capability.mail]
573
573
[get_inv]
574
574
) in
575
575
···
589
589
exit 1
590
590
| email :: _ ->
591
591
let thread_id = email.thread_id in
592
-
Jmap_eio.Cli.debug cfg "Thread ID: %s" (Jmap_proto.Id.to_string thread_id);
592
+
Jmap_eio.Cli.debug cfg "Thread ID: %s" (Jmap.Proto.Id.to_string thread_id);
593
593
594
594
(* Get the thread *)
595
595
let thread_inv = Jmap_eio.Client.Build.thread_get
···
600
600
in
601
601
let req2 = Jmap_eio.Client.Build.(
602
602
make_request
603
-
~capabilities:[Jmap_proto.Capability.core; Jmap_proto.Capability.mail]
603
+
~capabilities:[Jmap.Proto.Capability.core; Jmap.Proto.Capability.mail]
604
604
[thread_inv]
605
605
) in
606
606
···
622
622
let email_ids = thread.email_ids in
623
623
Fmt.pr "@[<v>%a %s (%d emails)@,@,"
624
624
Fmt.(styled `Bold string) "Thread"
625
-
(Jmap_proto.Id.to_string thread.id)
625
+
(Jmap.Proto.Id.to_string thread.id)
626
626
(List.length email_ids);
627
627
628
628
(* Fetch all emails in thread *)
···
636
636
in
637
637
let req3 = Jmap_eio.Client.Build.(
638
638
make_request
639
-
~capabilities:[Jmap_proto.Capability.core; Jmap_proto.Capability.mail]
639
+
~capabilities:[Jmap.Proto.Capability.core; Jmap.Proto.Capability.mail]
640
640
[get_inv2]
641
641
) in
642
642
···
650
650
Fmt.epr "Parse error: %s@." (Jsont.Error.to_string e);
651
651
exit 1
652
652
| Ok emails_result ->
653
-
List.iter (fun (email : Jmap_mail.Email.t) ->
653
+
List.iter (fun (email : Jmap.Proto.Email.t) ->
654
654
let from_str = match email.from with
655
655
| Some addrs -> format_email_addresses addrs
656
656
| None -> "(unknown)"
657
657
in
658
658
let subject = Option.value email.subject ~default:"(no subject)" in
659
659
Fmt.pr " %a %s@,"
660
-
Fmt.(styled `Cyan string) (Jmap_proto.Id.to_string email.id)
660
+
Fmt.(styled `Cyan string) (Jmap.Proto.Id.to_string email.id)
661
661
(ptime_to_string email.received_at);
662
662
Fmt.pr " From: %s@," (truncate_string 60 from_str);
663
663
Fmt.pr " Subject: %a@,@,"
···
680
680
681
681
let req = Jmap_eio.Client.Build.(
682
682
make_request
683
-
~capabilities:[Jmap_proto.Capability.core; Jmap_proto.Capability.mail;
684
-
Jmap_proto.Capability.submission]
683
+
~capabilities:[Jmap.Proto.Capability.core; Jmap.Proto.Capability.mail;
684
+
Jmap.Proto.Capability.submission]
685
685
[identity_get ~call_id:"i1" ~account_id ()]
686
686
) in
687
687
···
691
691
exit 1
692
692
| Ok response ->
693
693
match Jmap_eio.Client.Parse.parse_response ~call_id:"i1"
694
-
(Jmap_eio.Client.Parse.get_response Jmap_mail.Identity.jsont)
694
+
(Jmap_eio.Client.Parse.get_response Jmap.Proto.Identity.jsont)
695
695
response with
696
696
| Error e ->
697
697
Fmt.epr "Parse error: %s@." (Jsont.Error.to_string e);
···
700
700
Fmt.pr "@[<v>%a (state: %s)@,@,"
701
701
Fmt.(styled `Bold string) "Identities"
702
702
result.state;
703
-
List.iter (fun (ident : Jmap_mail.Identity.t) ->
703
+
List.iter (fun (ident : Jmap.Proto.Identity.t) ->
704
704
Fmt.pr " %a@,"
705
-
Fmt.(styled `Cyan string) (Jmap_proto.Id.to_string ident.id);
705
+
Fmt.(styled `Cyan string) (Jmap.Proto.Id.to_string ident.id);
706
706
Fmt.pr " Name: %s@," ident.name;
707
707
Fmt.pr " Email: %a@,"
708
708
Fmt.(styled `Green string) ident.email;
+8
-8
bin/tutorial_examples.ml
+8
-8
bin/tutorial_examples.ml
···
2
2
3
3
open Lwt.Syntax
4
4
open Jmap
5
-
open Jmap_mail
5
+
open Jmap.Mail
6
6
7
7
(* Example: Authentication *)
8
8
let auth_example () =
···
13
13
Printf.eprintf "Error: JMAP_API_TOKEN environment variable not set\n";
14
14
Lwt.return_none
15
15
| Some token ->
16
-
let+ result = Jmap_mail.login_with_token
16
+
let+ result = Jmap.Proto.login_with_token
17
17
~uri:"https://api.fastmail.com/jmap/session"
18
18
~api_token:token
19
19
in
···
23
23
| Ok conn ->
24
24
(* Get the primary account ID *)
25
25
let account_id =
26
-
let mail_capability = Jmap_mail.Capability.to_string Jmap_mail.Capability.Mail in
26
+
let mail_capability = Jmap.Proto.Capability.to_string Jmap.Proto.Capability.Mail in
27
27
match List.assoc_opt mail_capability conn.session.primary_accounts with
28
28
| Some id -> id
29
29
| None ->
···
45
45
(* Example: Working with Mailboxes *)
46
46
let mailbox_example (conn, account_id) =
47
47
(* Get all mailboxes *)
48
-
let+ mailboxes_result = Jmap_mail.get_mailboxes conn ~account_id in
48
+
let+ mailboxes_result = Jmap.Proto.get_mailboxes conn ~account_id in
49
49
50
50
match mailboxes_result with
51
51
| Ok mailboxes ->
···
78
78
(* Example: Working with Emails *)
79
79
let email_example (conn, account_id, mailbox_id) =
80
80
(* Get emails from mailbox *)
81
-
let+ emails_result = Jmap_mail.get_messages_in_mailbox
81
+
let+ emails_result = Jmap.Proto.get_messages_in_mailbox
82
82
conn
83
83
~account_id
84
84
~mailbox_id
···
91
91
Printf.printf "Found %d emails\n" (List.length emails);
92
92
93
93
(* Display emails *)
94
-
List.iter (fun (email:Jmap_mail.Types.email) ->
94
+
List.iter (fun (email:Jmap.Proto.Types.email) ->
95
95
(* Using explicit module path for Types to avoid ambiguity *)
96
-
let module Mail = Jmap_mail.Types in
96
+
let module Mail = Jmap.Proto.Types in
97
97
98
98
(* Get sender info *)
99
99
let from = match email.Mail.from with
···
127
127
128
128
match emails with
129
129
| [] -> None
130
-
| hd::_ -> Some (conn, account_id, hd.Jmap_mail.Types.id)
130
+
| hd::_ -> Some (conn, account_id, hd.Jmap.Proto.Types.id)
131
131
end
132
132
| Error e ->
133
133
Printf.eprintf "Error getting emails: %s\n"
+2
-2
eio/cli.ml
+2
-2
eio/cli.ml
···
197
197
198
198
let get_account_id cfg client =
199
199
match cfg.account_id with
200
-
| Some id -> Jmap_proto.Id.of_string_exn id
200
+
| Some id -> Jmap.Proto.Id.of_string_exn id
201
201
| None ->
202
202
let session = Client.session client in
203
-
match Jmap_proto.Session.primary_account_for Jmap_proto.Capability.mail session with
203
+
match Jmap.Proto.Session.primary_account_for Jmap.Proto.Capability.mail session with
204
204
| Some id -> id
205
205
| None ->
206
206
Fmt.epr "@[<v>%a No primary mail account found. Specify --account.@]@."
+1
-1
eio/cli.mli
+1
-1
eio/cli.mli
···
86
86
(** [create_client ~sw env cfg] creates a JMAP client from the configuration.
87
87
Exits with error message on connection failure. *)
88
88
89
-
val get_account_id : config -> Client.t -> Jmap_proto.Id.t
89
+
val get_account_id : config -> Client.t -> Jmap.Proto.Id.t
90
90
(** [get_account_id cfg client] returns the account ID from config, or the
91
91
primary mail account if not specified. Exits with error if no account found. *)
92
92
+20
-20
eio/client.ml
+20
-20
eio/client.ml
···
5
5
6
6
type error =
7
7
| Http_error of int * string
8
-
| Jmap_error of Jmap_proto.Error.Request_error.t
8
+
| Jmap_error of Jmap.Proto.Error.Request_error.t
9
9
| Json_error of Jsont.Error.t
10
10
| Session_error of string
11
11
| Connection_error of string
···
15
15
Format.fprintf fmt "HTTP error %d: %s" code msg
16
16
| Jmap_error err ->
17
17
Format.fprintf fmt "JMAP error: %s"
18
-
(Jmap_proto.Error.Request_error.urn_to_string err.type_)
18
+
(Jmap.Proto.Error.Request_error.urn_to_string err.type_)
19
19
| Json_error err ->
20
20
Format.fprintf fmt "JSON error: %s" (Jsont.Error.to_string err)
21
21
| Session_error msg ->
···
29
29
exception Jmap_client_error of error
30
30
31
31
type t = {
32
-
mutable session : Jmap_proto.Session.t;
32
+
mutable session : Jmap.Proto.Session.t;
33
33
requests : Requests.t;
34
34
auth : Requests.Auth.t option;
35
35
session_url : string;
36
36
}
37
37
38
38
let session t = t.session
39
-
let api_url t = Jmap_proto.Session.api_url t.session
40
-
let upload_url t = Jmap_proto.Session.upload_url t.session
41
-
let download_url t = Jmap_proto.Session.download_url t.session
39
+
let api_url t = Jmap.Proto.Session.api_url t.session
40
+
let upload_url t = Jmap.Proto.Session.upload_url t.session
41
+
let download_url t = Jmap.Proto.Session.download_url t.session
42
42
43
43
let create ?auth ~session requests =
44
-
let session_url = Jmap_proto.Session.api_url session in
44
+
let session_url = Jmap.Proto.Session.api_url session in
45
45
{ session; requests; auth; session_url }
46
46
47
47
let fetch_session ?auth requests url =
···
119
119
120
120
let expand_upload_url t ~account_id =
121
121
let template = upload_url t in
122
-
let account_id_str = Jmap_proto.Id.to_string account_id in
122
+
let account_id_str = Jmap.Proto.Id.to_string account_id in
123
123
(* Simple template expansion for {accountId} *)
124
124
let re = Str.regexp "{accountId}" in
125
125
Str.global_replace re account_id_str template
···
154
154
155
155
let expand_download_url t ~account_id ~blob_id ?name ?accept () =
156
156
let template = download_url t in
157
-
let account_id_str = Jmap_proto.Id.to_string account_id in
158
-
let blob_id_str = Jmap_proto.Id.to_string blob_id in
157
+
let account_id_str = Jmap.Proto.Id.to_string account_id in
158
+
let blob_id_str = Jmap.Proto.Id.to_string blob_id in
159
159
let name_str = Option.value name ~default:"download" in
160
160
let type_str = Option.value accept ~default:"application/octet-stream" in
161
161
(* Simple template expansion *)
···
190
190
191
191
(* Convenience builders *)
192
192
module Build = struct
193
-
open Jmap_proto
193
+
open Jmap.Proto
194
194
195
195
let json_of_id id =
196
196
Jsont.String (Id.to_string id, Jsont.Meta.none)
···
264
264
let args = match filter with
265
265
| None -> args
266
266
| Some f ->
267
-
("filter", encode_to_json Jmap_mail.Mail_filter.mailbox_filter_jsont f) :: args
267
+
("filter", encode_to_json Jmap.Proto.Mail_filter.mailbox_filter_jsont f) :: args
268
268
in
269
269
let args = match sort with
270
270
| None -> args
···
336
336
let args = match filter with
337
337
| None -> args
338
338
| Some f ->
339
-
("filter", encode_to_json Jmap_mail.Mail_filter.email_filter_jsont f) :: args
339
+
("filter", encode_to_json Jmap.Proto.Mail_filter.email_filter_jsont f) :: args
340
340
in
341
341
let args = match sort with
342
342
| None -> args
···
413
413
let args = match filter with
414
414
| None -> args
415
415
| Some f ->
416
-
("filter", encode_to_json Jmap_mail.Mail_filter.submission_filter_jsont f) :: args
416
+
("filter", encode_to_json Jmap.Proto.Mail_filter.submission_filter_jsont f) :: args
417
417
in
418
418
let args = match sort with
419
419
| None -> args
···
433
433
let vacation_response_get ~call_id ~account_id () =
434
434
let args = [
435
435
("accountId", json_of_id account_id);
436
-
("ids", json_of_id_list [Jmap_mail.Vacation.singleton_id]);
436
+
("ids", json_of_id_list [Jmap.Proto.Vacation.singleton_id]);
437
437
] in
438
438
make_invocation ~name:"VacationResponse/get" ~call_id args
439
439
···
447
447
448
448
(* Response parsing helpers *)
449
449
module Parse = struct
450
-
open Jmap_proto
450
+
open Jmap.Proto
451
451
452
452
let decode_from_json jsont json =
453
453
Jsont.Json.decode' jsont json
···
484
484
(* Mail-specific parsers *)
485
485
486
486
let mailbox_get_response =
487
-
get_response Jmap_mail.Mailbox.jsont
487
+
get_response Jmap.Proto.Mailbox.jsont
488
488
489
489
let email_get_response =
490
-
get_response Jmap_mail.Email.jsont
490
+
get_response Jmap.Proto.Email.jsont
491
491
492
492
let thread_get_response =
493
-
get_response Jmap_mail.Thread.jsont
493
+
get_response Jmap.Proto.Thread.jsont
494
494
495
495
let identity_get_response =
496
-
get_response Jmap_mail.Identity.jsont
496
+
get_response Jmap.Proto.Identity.jsont
497
497
498
498
(* Convenience functions *)
499
499
+78
-78
eio/client.mli
+78
-78
eio/client.mli
···
16
16
type error =
17
17
| Http_error of int * string
18
18
(** HTTP error with status code and message. *)
19
-
| Jmap_error of Jmap_proto.Error.Request_error.t
19
+
| Jmap_error of Jmap.Proto.Error.Request_error.t
20
20
(** JMAP protocol error at request level. *)
21
21
| Json_error of Jsont.Error.t
22
22
(** JSON encoding/decoding error. *)
···
39
39
40
40
val create :
41
41
?auth:Requests.Auth.t ->
42
-
session:Jmap_proto.Session.t ->
42
+
session:Jmap.Proto.Session.t ->
43
43
Requests.t ->
44
44
t
45
45
(** [create ?auth ~session requests] creates a JMAP client from an existing
···
75
75
76
76
(** {1 Session Access} *)
77
77
78
-
val session : t -> Jmap_proto.Session.t
78
+
val session : t -> Jmap.Proto.Session.t
79
79
(** [session client] returns the current JMAP session. *)
80
80
81
81
val refresh_session : t -> (unit, error) result
···
98
98
99
99
val request :
100
100
t ->
101
-
Jmap_proto.Request.t ->
102
-
(Jmap_proto.Response.t, error) result
101
+
Jmap.Proto.Request.t ->
102
+
(Jmap.Proto.Response.t, error) result
103
103
(** [request client req] executes a JMAP request and returns the response. *)
104
104
105
105
val request_exn :
106
106
t ->
107
-
Jmap_proto.Request.t ->
108
-
Jmap_proto.Response.t
107
+
Jmap.Proto.Request.t ->
108
+
Jmap.Proto.Response.t
109
109
(** [request_exn client req] is like {!request} but raises on error. *)
110
110
111
111
(** {1 Blob Operations} *)
112
112
113
113
val upload :
114
114
t ->
115
-
account_id:Jmap_proto.Id.t ->
115
+
account_id:Jmap.Proto.Id.t ->
116
116
content_type:string ->
117
117
data:string ->
118
-
(Jmap_proto.Blob.upload_response, error) result
118
+
(Jmap.Proto.Blob.upload_response, error) result
119
119
(** [upload client ~account_id ~content_type ~data] uploads a blob.
120
120
121
121
@param account_id The account to upload to.
···
124
124
125
125
val upload_exn :
126
126
t ->
127
-
account_id:Jmap_proto.Id.t ->
127
+
account_id:Jmap.Proto.Id.t ->
128
128
content_type:string ->
129
129
data:string ->
130
-
Jmap_proto.Blob.upload_response
130
+
Jmap.Proto.Blob.upload_response
131
131
(** [upload_exn client ~account_id ~content_type ~data] is like {!upload}
132
132
but raises on error. *)
133
133
134
134
val download :
135
135
t ->
136
-
account_id:Jmap_proto.Id.t ->
137
-
blob_id:Jmap_proto.Id.t ->
136
+
account_id:Jmap.Proto.Id.t ->
137
+
blob_id:Jmap.Proto.Id.t ->
138
138
?name:string ->
139
139
?accept:string ->
140
140
unit ->
···
148
148
149
149
val download_exn :
150
150
t ->
151
-
account_id:Jmap_proto.Id.t ->
152
-
blob_id:Jmap_proto.Id.t ->
151
+
account_id:Jmap.Proto.Id.t ->
152
+
blob_id:Jmap.Proto.Id.t ->
153
153
?name:string ->
154
154
?accept:string ->
155
155
unit ->
···
166
166
val echo :
167
167
call_id:string ->
168
168
Jsont.json ->
169
-
Jmap_proto.Invocation.t
169
+
Jmap.Proto.Invocation.t
170
170
(** [echo ~call_id data] builds a Core/echo invocation. *)
171
171
172
172
(** {2 Mailbox Methods} *)
173
173
174
174
val mailbox_get :
175
175
call_id:string ->
176
-
account_id:Jmap_proto.Id.t ->
177
-
?ids:Jmap_proto.Id.t list ->
176
+
account_id:Jmap.Proto.Id.t ->
177
+
?ids:Jmap.Proto.Id.t list ->
178
178
?properties:string list ->
179
179
unit ->
180
-
Jmap_proto.Invocation.t
180
+
Jmap.Proto.Invocation.t
181
181
(** [mailbox_get ~call_id ~account_id ?ids ?properties ()] builds a
182
182
Mailbox/get invocation. *)
183
183
184
184
val mailbox_changes :
185
185
call_id:string ->
186
-
account_id:Jmap_proto.Id.t ->
186
+
account_id:Jmap.Proto.Id.t ->
187
187
since_state:string ->
188
188
?max_changes:int64 ->
189
189
unit ->
190
-
Jmap_proto.Invocation.t
190
+
Jmap.Proto.Invocation.t
191
191
(** [mailbox_changes ~call_id ~account_id ~since_state ?max_changes ()]
192
192
builds a Mailbox/changes invocation. *)
193
193
194
194
val mailbox_query :
195
195
call_id:string ->
196
-
account_id:Jmap_proto.Id.t ->
197
-
?filter:Jmap_mail.Mail_filter.mailbox_filter ->
198
-
?sort:Jmap_proto.Filter.comparator list ->
196
+
account_id:Jmap.Proto.Id.t ->
197
+
?filter:Jmap.Proto.Mail_filter.mailbox_filter ->
198
+
?sort:Jmap.Proto.Filter.comparator list ->
199
199
?position:int64 ->
200
200
?limit:int64 ->
201
201
unit ->
202
-
Jmap_proto.Invocation.t
202
+
Jmap.Proto.Invocation.t
203
203
(** [mailbox_query ~call_id ~account_id ?filter ?sort ?position ?limit ()]
204
204
builds a Mailbox/query invocation. *)
205
205
···
207
207
208
208
val email_get :
209
209
call_id:string ->
210
-
account_id:Jmap_proto.Id.t ->
211
-
?ids:Jmap_proto.Id.t list ->
210
+
account_id:Jmap.Proto.Id.t ->
211
+
?ids:Jmap.Proto.Id.t list ->
212
212
?properties:string list ->
213
213
?body_properties:string list ->
214
214
?fetch_text_body_values:bool ->
···
216
216
?fetch_all_body_values:bool ->
217
217
?max_body_value_bytes:int64 ->
218
218
unit ->
219
-
Jmap_proto.Invocation.t
219
+
Jmap.Proto.Invocation.t
220
220
(** [email_get ~call_id ~account_id ?ids ?properties ...] builds an
221
221
Email/get invocation. *)
222
222
223
223
val email_changes :
224
224
call_id:string ->
225
-
account_id:Jmap_proto.Id.t ->
225
+
account_id:Jmap.Proto.Id.t ->
226
226
since_state:string ->
227
227
?max_changes:int64 ->
228
228
unit ->
229
-
Jmap_proto.Invocation.t
229
+
Jmap.Proto.Invocation.t
230
230
(** [email_changes ~call_id ~account_id ~since_state ?max_changes ()]
231
231
builds an Email/changes invocation. *)
232
232
233
233
val email_query :
234
234
call_id:string ->
235
-
account_id:Jmap_proto.Id.t ->
236
-
?filter:Jmap_mail.Mail_filter.email_filter ->
237
-
?sort:Jmap_proto.Filter.comparator list ->
235
+
account_id:Jmap.Proto.Id.t ->
236
+
?filter:Jmap.Proto.Mail_filter.email_filter ->
237
+
?sort:Jmap.Proto.Filter.comparator list ->
238
238
?position:int64 ->
239
239
?limit:int64 ->
240
240
?collapse_threads:bool ->
241
241
unit ->
242
-
Jmap_proto.Invocation.t
242
+
Jmap.Proto.Invocation.t
243
243
(** [email_query ~call_id ~account_id ?filter ?sort ?position ?limit
244
244
?collapse_threads ()] builds an Email/query invocation. *)
245
245
···
247
247
248
248
val thread_get :
249
249
call_id:string ->
250
-
account_id:Jmap_proto.Id.t ->
251
-
?ids:Jmap_proto.Id.t list ->
250
+
account_id:Jmap.Proto.Id.t ->
251
+
?ids:Jmap.Proto.Id.t list ->
252
252
unit ->
253
-
Jmap_proto.Invocation.t
253
+
Jmap.Proto.Invocation.t
254
254
(** [thread_get ~call_id ~account_id ?ids ()] builds a Thread/get invocation. *)
255
255
256
256
val thread_changes :
257
257
call_id:string ->
258
-
account_id:Jmap_proto.Id.t ->
258
+
account_id:Jmap.Proto.Id.t ->
259
259
since_state:string ->
260
260
?max_changes:int64 ->
261
261
unit ->
262
-
Jmap_proto.Invocation.t
262
+
Jmap.Proto.Invocation.t
263
263
(** [thread_changes ~call_id ~account_id ~since_state ?max_changes ()]
264
264
builds a Thread/changes invocation. *)
265
265
···
267
267
268
268
val identity_get :
269
269
call_id:string ->
270
-
account_id:Jmap_proto.Id.t ->
271
-
?ids:Jmap_proto.Id.t list ->
270
+
account_id:Jmap.Proto.Id.t ->
271
+
?ids:Jmap.Proto.Id.t list ->
272
272
?properties:string list ->
273
273
unit ->
274
-
Jmap_proto.Invocation.t
274
+
Jmap.Proto.Invocation.t
275
275
(** [identity_get ~call_id ~account_id ?ids ?properties ()] builds an
276
276
Identity/get invocation. *)
277
277
···
279
279
280
280
val email_submission_get :
281
281
call_id:string ->
282
-
account_id:Jmap_proto.Id.t ->
283
-
?ids:Jmap_proto.Id.t list ->
282
+
account_id:Jmap.Proto.Id.t ->
283
+
?ids:Jmap.Proto.Id.t list ->
284
284
?properties:string list ->
285
285
unit ->
286
-
Jmap_proto.Invocation.t
286
+
Jmap.Proto.Invocation.t
287
287
(** [email_submission_get ~call_id ~account_id ?ids ?properties ()]
288
288
builds an EmailSubmission/get invocation. *)
289
289
290
290
val email_submission_query :
291
291
call_id:string ->
292
-
account_id:Jmap_proto.Id.t ->
293
-
?filter:Jmap_mail.Mail_filter.submission_filter ->
294
-
?sort:Jmap_proto.Filter.comparator list ->
292
+
account_id:Jmap.Proto.Id.t ->
293
+
?filter:Jmap.Proto.Mail_filter.submission_filter ->
294
+
?sort:Jmap.Proto.Filter.comparator list ->
295
295
?position:int64 ->
296
296
?limit:int64 ->
297
297
unit ->
298
-
Jmap_proto.Invocation.t
298
+
Jmap.Proto.Invocation.t
299
299
(** [email_submission_query ~call_id ~account_id ?filter ?sort ?position
300
300
?limit ()] builds an EmailSubmission/query invocation. *)
301
301
···
303
303
304
304
val vacation_response_get :
305
305
call_id:string ->
306
-
account_id:Jmap_proto.Id.t ->
306
+
account_id:Jmap.Proto.Id.t ->
307
307
unit ->
308
-
Jmap_proto.Invocation.t
308
+
Jmap.Proto.Invocation.t
309
309
(** [vacation_response_get ~call_id ~account_id ()] builds a
310
310
VacationResponse/get invocation. The singleton ID is automatically used. *)
311
311
312
312
(** {2 Request Building} *)
313
313
314
314
val make_request :
315
-
?created_ids:(Jmap_proto.Id.t * Jmap_proto.Id.t) list ->
315
+
?created_ids:(Jmap.Proto.Id.t * Jmap.Proto.Id.t) list ->
316
316
capabilities:string list ->
317
-
Jmap_proto.Invocation.t list ->
318
-
Jmap_proto.Request.t
317
+
Jmap.Proto.Invocation.t list ->
318
+
Jmap.Proto.Request.t
319
319
(** [make_request ?created_ids ~capabilities invocations] builds a JMAP request.
320
320
321
321
@param created_ids Optional client-created ID mappings.
···
330
330
module Parse : sig
331
331
val find_invocation :
332
332
call_id:string ->
333
-
Jmap_proto.Response.t ->
334
-
Jmap_proto.Invocation.t option
333
+
Jmap.Proto.Response.t ->
334
+
Jmap.Proto.Invocation.t option
335
335
(** [find_invocation ~call_id response] finds an invocation by call ID. *)
336
336
337
337
val get_invocation_exn :
338
338
call_id:string ->
339
-
Jmap_proto.Response.t ->
340
-
Jmap_proto.Invocation.t
339
+
Jmap.Proto.Response.t ->
340
+
Jmap.Proto.Invocation.t
341
341
(** [get_invocation_exn ~call_id response] finds an invocation by call ID.
342
342
@raise Failure if not found. *)
343
343
344
344
val parse_invocation :
345
345
'a Jsont.t ->
346
-
Jmap_proto.Invocation.t ->
346
+
Jmap.Proto.Invocation.t ->
347
347
('a, Jsont.Error.t) result
348
348
(** [parse_invocation jsont inv] decodes the invocation's arguments. *)
349
349
350
350
val parse_response :
351
351
call_id:string ->
352
352
'a Jsont.t ->
353
-
Jmap_proto.Response.t ->
353
+
Jmap.Proto.Response.t ->
354
354
('a, Jsont.Error.t) result
355
355
(** [parse_response ~call_id jsont response] finds and parses an invocation. *)
356
356
357
357
(** {2 Typed Response Codecs} *)
358
358
359
-
val get_response : 'a Jsont.t -> 'a Jmap_proto.Method.get_response Jsont.t
359
+
val get_response : 'a Jsont.t -> 'a Jmap.Proto.Method.get_response Jsont.t
360
360
(** [get_response obj_jsont] creates a Foo/get response codec. *)
361
361
362
-
val query_response : Jmap_proto.Method.query_response Jsont.t
362
+
val query_response : Jmap.Proto.Method.query_response Jsont.t
363
363
(** Codec for Foo/query responses. *)
364
364
365
-
val changes_response : Jmap_proto.Method.changes_response Jsont.t
365
+
val changes_response : Jmap.Proto.Method.changes_response Jsont.t
366
366
(** Codec for Foo/changes responses. *)
367
367
368
-
val set_response : 'a Jsont.t -> 'a Jmap_proto.Method.set_response Jsont.t
368
+
val set_response : 'a Jsont.t -> 'a Jmap.Proto.Method.set_response Jsont.t
369
369
(** [set_response obj_jsont] creates a Foo/set response codec. *)
370
370
371
371
(** {2 Mail-specific Codecs} *)
372
372
373
-
val mailbox_get_response : Jmap_mail.Mailbox.t Jmap_proto.Method.get_response Jsont.t
374
-
val email_get_response : Jmap_mail.Email.t Jmap_proto.Method.get_response Jsont.t
375
-
val thread_get_response : Jmap_mail.Thread.t Jmap_proto.Method.get_response Jsont.t
376
-
val identity_get_response : Jmap_mail.Identity.t Jmap_proto.Method.get_response Jsont.t
373
+
val mailbox_get_response : Jmap.Proto.Mailbox.t Jmap.Proto.Method.get_response Jsont.t
374
+
val email_get_response : Jmap.Proto.Email.t Jmap.Proto.Method.get_response Jsont.t
375
+
val thread_get_response : Jmap.Proto.Thread.t Jmap.Proto.Method.get_response Jsont.t
376
+
val identity_get_response : Jmap.Proto.Identity.t Jmap.Proto.Method.get_response Jsont.t
377
377
378
378
(** {2 Convenience Parsers} *)
379
379
380
380
val parse_mailbox_get :
381
381
call_id:string ->
382
-
Jmap_proto.Response.t ->
383
-
(Jmap_mail.Mailbox.t Jmap_proto.Method.get_response, Jsont.Error.t) result
382
+
Jmap.Proto.Response.t ->
383
+
(Jmap.Proto.Mailbox.t Jmap.Proto.Method.get_response, Jsont.Error.t) result
384
384
385
385
val parse_email_get :
386
386
call_id:string ->
387
-
Jmap_proto.Response.t ->
388
-
(Jmap_mail.Email.t Jmap_proto.Method.get_response, Jsont.Error.t) result
387
+
Jmap.Proto.Response.t ->
388
+
(Jmap.Proto.Email.t Jmap.Proto.Method.get_response, Jsont.Error.t) result
389
389
390
390
val parse_email_query :
391
391
call_id:string ->
392
-
Jmap_proto.Response.t ->
393
-
(Jmap_proto.Method.query_response, Jsont.Error.t) result
392
+
Jmap.Proto.Response.t ->
393
+
(Jmap.Proto.Method.query_response, Jsont.Error.t) result
394
394
395
395
val parse_thread_get :
396
396
call_id:string ->
397
-
Jmap_proto.Response.t ->
398
-
(Jmap_mail.Thread.t Jmap_proto.Method.get_response, Jsont.Error.t) result
397
+
Jmap.Proto.Response.t ->
398
+
(Jmap.Proto.Thread.t Jmap.Proto.Method.get_response, Jsont.Error.t) result
399
399
400
400
val parse_changes :
401
401
call_id:string ->
402
-
Jmap_proto.Response.t ->
403
-
(Jmap_proto.Method.changes_response, Jsont.Error.t) result
402
+
Jmap.Proto.Response.t ->
403
+
(Jmap.Proto.Method.changes_response, Jsont.Error.t) result
404
404
end
+4
-4
eio/codec.ml
+4
-4
eio/codec.ml
···
10
10
Jsont_bytesrw.decode_string' ?locs jsont json
11
11
12
12
let encode_request ?format request =
13
-
encode ?format Jmap_proto.Request.jsont request
13
+
encode ?format Jmap.Proto.Request.jsont request
14
14
15
15
let encode_request_exn ?format request =
16
16
match encode_request ?format request with
···
18
18
| Error e -> failwith (Jsont.Error.to_string e)
19
19
20
20
let decode_response ?locs json =
21
-
decode ?locs Jmap_proto.Response.jsont json
21
+
decode ?locs Jmap.Proto.Response.jsont json
22
22
23
23
let decode_response_exn ?locs json =
24
24
match decode_response ?locs json with
···
26
26
| Error e -> failwith (Jsont.Error.to_string e)
27
27
28
28
let decode_session ?locs json =
29
-
decode ?locs Jmap_proto.Session.jsont json
29
+
decode ?locs Jmap.Proto.Session.jsont json
30
30
31
31
let decode_session_exn ?locs json =
32
32
match decode_session ?locs json with
···
34
34
| Error e -> failwith (Jsont.Error.to_string e)
35
35
36
36
let decode_upload_response ?locs json =
37
-
decode ?locs Jmap_proto.Blob.upload_response_jsont json
37
+
decode ?locs Jmap.Proto.Blob.upload_response_jsont json
38
38
39
39
let decode_upload_response_exn ?locs json =
40
40
match decode_upload_response ?locs json with
+8
-8
eio/codec.mli
+8
-8
eio/codec.mli
···
11
11
12
12
val encode_request :
13
13
?format:Jsont.format ->
14
-
Jmap_proto.Request.t ->
14
+
Jmap.Proto.Request.t ->
15
15
(string, Jsont.Error.t) result
16
16
(** [encode_request ?format request] encodes a JMAP request to a JSON string.
17
17
···
19
19
20
20
val encode_request_exn :
21
21
?format:Jsont.format ->
22
-
Jmap_proto.Request.t ->
22
+
Jmap.Proto.Request.t ->
23
23
string
24
24
(** [encode_request_exn ?format request] is like {!encode_request} but raises
25
25
on encoding errors. *)
···
29
29
val decode_response :
30
30
?locs:bool ->
31
31
string ->
32
-
(Jmap_proto.Response.t, Jsont.Error.t) result
32
+
(Jmap.Proto.Response.t, Jsont.Error.t) result
33
33
(** [decode_response ?locs json] decodes a JMAP response from a JSON string.
34
34
35
35
@param locs If [true], location information is preserved for error messages.
···
38
38
val decode_response_exn :
39
39
?locs:bool ->
40
40
string ->
41
-
Jmap_proto.Response.t
41
+
Jmap.Proto.Response.t
42
42
(** [decode_response_exn ?locs json] is like {!decode_response} but raises
43
43
on decoding errors. *)
44
44
···
47
47
val decode_session :
48
48
?locs:bool ->
49
49
string ->
50
-
(Jmap_proto.Session.t, Jsont.Error.t) result
50
+
(Jmap.Proto.Session.t, Jsont.Error.t) result
51
51
(** [decode_session ?locs json] decodes a JMAP session from a JSON string.
52
52
53
53
@param locs If [true], location information is preserved for error messages.
···
56
56
val decode_session_exn :
57
57
?locs:bool ->
58
58
string ->
59
-
Jmap_proto.Session.t
59
+
Jmap.Proto.Session.t
60
60
(** [decode_session_exn ?locs json] is like {!decode_session} but raises
61
61
on decoding errors. *)
62
62
···
65
65
val decode_upload_response :
66
66
?locs:bool ->
67
67
string ->
68
-
(Jmap_proto.Blob.upload_response, Jsont.Error.t) result
68
+
(Jmap.Proto.Blob.upload_response, Jsont.Error.t) result
69
69
(** [decode_upload_response ?locs json] decodes a blob upload response. *)
70
70
71
71
val decode_upload_response_exn :
72
72
?locs:bool ->
73
73
string ->
74
-
Jmap_proto.Blob.upload_response
74
+
Jmap.Proto.Blob.upload_response
75
75
(** [decode_upload_response_exn ?locs json] is like {!decode_upload_response}
76
76
but raises on decoding errors. *)
77
77
+1
-1
eio/dune
+1
-1
eio/dune
+7
-7
eio/jmap_eio.mli
+7
-7
eio/jmap_eio.mli
···
34
34
35
35
(* Get session info *)
36
36
let session = Jmap_eio.Client.session client in
37
-
Printf.printf "API URL: %s\n" (Jmap_proto.Session.api_url session);
37
+
Printf.printf "API URL: %s\n" (Jmap.Proto.Session.api_url session);
38
38
39
39
(* Build and execute a request *)
40
40
let account_id = (* get from session *) ... in
41
41
let req = Jmap_eio.Client.Build.(
42
42
make_request
43
-
~capabilities:[Jmap_proto.Capability.core_uri;
44
-
Jmap_proto.Capability.mail_uri]
43
+
~capabilities:[Jmap.Proto.Capability.core_uri;
44
+
Jmap.Proto.Capability.mail_uri]
45
45
[mailbox_get ~call_id:"0" ~account_id ()]
46
46
) in
47
47
let response = Jmap_eio.Client.request_exn client req in
···
49
49
(* Process response *)
50
50
List.iter (fun inv ->
51
51
Printf.printf "Method: %s, CallId: %s\n"
52
-
(Jmap_proto.Invocation.name inv)
53
-
(Jmap_proto.Invocation.method_call_id inv)
54
-
) (Jmap_proto.Response.method_responses response)
52
+
(Jmap.Proto.Invocation.name inv)
53
+
(Jmap.Proto.Invocation.method_call_id inv)
54
+
) (Jmap.Proto.Response.method_responses response)
55
55
]}
56
56
57
57
{2 Capabilities}
···
63
63
- [urn:ietf:params:jmap:submission] - EmailSubmission
64
64
- [urn:ietf:params:jmap:vacationresponse] - VacationResponse
65
65
66
-
These are available as constants in {!Jmap_proto.Capability}.
66
+
These are available as constants in {!Jmap.Proto.Capability}.
67
67
*)
68
68
69
69
(** Low-level JSON codec for JMAP messages. *)
+493
lib/core/jmap.ml
+493
lib/core/jmap.ml
···
1
+
(*---------------------------------------------------------------------------
2
+
Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3
+
SPDX-License-Identifier: ISC
4
+
---------------------------------------------------------------------------*)
5
+
6
+
(** Unified JMAP interface for OCaml
7
+
8
+
This module provides a clean, ergonomic API for working with JMAP
9
+
(RFC 8620/8621), combining the protocol and mail layers with abstract
10
+
types and polymorphic variants.
11
+
12
+
{2 Quick Start}
13
+
14
+
{[
15
+
open Jmap
16
+
17
+
(* Keywords use polymorphic variants *)
18
+
let is_unread email =
19
+
not (List.mem `Seen (Email.keywords email))
20
+
21
+
(* Mailbox roles are also polymorphic *)
22
+
let find_inbox mailboxes =
23
+
List.find_opt (fun m -> Mailbox.role m = Some `Inbox) mailboxes
24
+
]}
25
+
26
+
{2 Module Structure}
27
+
28
+
- {!Proto} - Low-level protocol and mail types (RFC 8620/8621)
29
+
- {!Error}, {!Id}, {!Keyword}, {!Role}, {!Capability} - Core types
30
+
- {!Session}, {!Email}, {!Mailbox}, etc. - Abstract type accessors
31
+
*)
32
+
33
+
(** {1 Protocol Layer Re-exports} *)
34
+
35
+
(** Low-level JMAP protocol types (RFC 8620/8621).
36
+
37
+
These are the raw protocol and mail types. For most use cases, prefer the
38
+
higher-level types in this module. *)
39
+
module Proto = Jmap_proto
40
+
41
+
(** {1 Core Types} *)
42
+
43
+
(** Unified error type for JMAP operations.
44
+
45
+
All errors from JSON parsing, HTTP, session management, and JMAP method
46
+
calls are represented as polymorphic variants. *)
47
+
module Error = Jmap_types.Error
48
+
49
+
(** JMAP identifier type.
50
+
51
+
Identifiers are opaque strings assigned by the server. *)
52
+
module Id = Jmap_types.Id
53
+
54
+
(** Email keyword type.
55
+
56
+
Standard keywords are represented as polymorphic variants.
57
+
Custom keywords use [`Custom of string]. *)
58
+
module Keyword = Jmap_types.Keyword
59
+
60
+
(** Mailbox role type.
61
+
62
+
Standard roles are represented as polymorphic variants.
63
+
Custom roles use [`Custom of string]. *)
64
+
module Role = Jmap_types.Role
65
+
66
+
(** JMAP capability type.
67
+
68
+
Standard capabilities are represented as polymorphic variants.
69
+
Custom capabilities use [`Custom of string]. *)
70
+
module Capability = Jmap_types.Capability
71
+
72
+
(** {1 Session Types} *)
73
+
74
+
(** JMAP session information.
75
+
76
+
The session contains server capabilities, account information,
77
+
and API endpoint URLs. *)
78
+
module Session = struct
79
+
(** Account information. *)
80
+
module Account = struct
81
+
type t = Jmap_types.account
82
+
83
+
let name a = Proto.Session.Account.name a
84
+
let is_personal a = Proto.Session.Account.is_personal a
85
+
let is_read_only a = Proto.Session.Account.is_read_only a
86
+
end
87
+
88
+
type t = Jmap_types.session
89
+
90
+
let capabilities s = Proto.Session.capabilities s
91
+
let accounts s = Proto.Session.accounts s
92
+
let primary_accounts s = Proto.Session.primary_accounts s
93
+
let username s = Proto.Session.username s
94
+
let api_url s = Proto.Session.api_url s
95
+
let download_url s = Proto.Session.download_url s
96
+
let upload_url s = Proto.Session.upload_url s
97
+
let event_source_url s = Proto.Session.event_source_url s
98
+
let state s = Proto.Session.state s
99
+
100
+
let get_account id s = Proto.Session.get_account id s
101
+
let primary_account_for cap s = Proto.Session.primary_account_for cap s
102
+
let has_capability uri s = Proto.Session.has_capability uri s
103
+
end
104
+
105
+
(** {1 Mail Types} *)
106
+
107
+
(** Email address with optional display name. *)
108
+
module Email_address = struct
109
+
type t = Jmap_types.email_address
110
+
111
+
let name a = Proto.Email_address.name a
112
+
let email a = Proto.Email_address.email a
113
+
114
+
let create ?name email =
115
+
Proto.Email_address.create ?name email
116
+
end
117
+
118
+
(** Email mailbox. *)
119
+
module Mailbox = struct
120
+
type t = Jmap_types.mailbox
121
+
122
+
let id m = Proto.Mailbox.id m
123
+
let name m = Proto.Mailbox.name m
124
+
let parent_id m = Proto.Mailbox.parent_id m
125
+
let sort_order m = Proto.Mailbox.sort_order m
126
+
let total_emails m = Proto.Mailbox.total_emails m
127
+
let unread_emails m = Proto.Mailbox.unread_emails m
128
+
let total_threads m = Proto.Mailbox.total_threads m
129
+
let unread_threads m = Proto.Mailbox.unread_threads m
130
+
let is_subscribed m = Proto.Mailbox.is_subscribed m
131
+
132
+
let role m =
133
+
let convert_role = function
134
+
| Proto.Mailbox.Inbox -> `Inbox
135
+
| Proto.Mailbox.Sent -> `Sent
136
+
| Proto.Mailbox.Drafts -> `Drafts
137
+
| Proto.Mailbox.Trash -> `Trash
138
+
| Proto.Mailbox.Junk -> `Junk
139
+
| Proto.Mailbox.Archive -> `Archive
140
+
| Proto.Mailbox.Flagged -> `Flagged
141
+
| Proto.Mailbox.Important -> `Important
142
+
| Proto.Mailbox.All -> `All
143
+
| Proto.Mailbox.Subscribed -> `Subscribed
144
+
| Proto.Mailbox.Other s -> `Custom s
145
+
in
146
+
Option.map convert_role (Proto.Mailbox.role m)
147
+
148
+
(** Mailbox rights. *)
149
+
module Rights = struct
150
+
type t = Proto.Mailbox.Rights.t
151
+
152
+
let may_read_items r = Proto.Mailbox.Rights.may_read_items r
153
+
let may_add_items r = Proto.Mailbox.Rights.may_add_items r
154
+
let may_remove_items r = Proto.Mailbox.Rights.may_remove_items r
155
+
let may_set_seen r = Proto.Mailbox.Rights.may_set_seen r
156
+
let may_set_keywords r = Proto.Mailbox.Rights.may_set_keywords r
157
+
let may_create_child r = Proto.Mailbox.Rights.may_create_child r
158
+
let may_rename r = Proto.Mailbox.Rights.may_rename r
159
+
let may_delete r = Proto.Mailbox.Rights.may_delete r
160
+
let may_submit r = Proto.Mailbox.Rights.may_submit r
161
+
end
162
+
163
+
let my_rights m = Proto.Mailbox.my_rights m
164
+
end
165
+
166
+
(** Email thread. *)
167
+
module Thread = struct
168
+
type t = Jmap_types.thread
169
+
170
+
let id t = Proto.Thread.id t
171
+
let email_ids t = Proto.Thread.email_ids t
172
+
end
173
+
174
+
(** Email message. *)
175
+
module Email = struct
176
+
(** Email body part. *)
177
+
module Body = struct
178
+
type part = Proto.Email_body.Part.t
179
+
type value = Proto.Email_body.Value.t
180
+
181
+
let part_id p = Proto.Email_body.Part.part_id p
182
+
let blob_id p = Proto.Email_body.Part.blob_id p
183
+
let size p = Proto.Email_body.Part.size p
184
+
let name p = Proto.Email_body.Part.name p
185
+
let type_ p = Proto.Email_body.Part.type_ p
186
+
let charset p = Proto.Email_body.Part.charset p
187
+
let disposition p = Proto.Email_body.Part.disposition p
188
+
let cid p = Proto.Email_body.Part.cid p
189
+
let language p = Proto.Email_body.Part.language p
190
+
let location p = Proto.Email_body.Part.location p
191
+
192
+
let value_text v = Proto.Email_body.Value.value v
193
+
let value_is_truncated v = Proto.Email_body.Value.is_truncated v
194
+
let value_is_encoding_problem v = Proto.Email_body.Value.is_encoding_problem v
195
+
end
196
+
197
+
type t = Jmap_types.email
198
+
199
+
let id e = Proto.Email.id e
200
+
let blob_id e = Proto.Email.blob_id e
201
+
let thread_id e = Proto.Email.thread_id e
202
+
let mailbox_ids e = Proto.Email.mailbox_ids e
203
+
let size e = Proto.Email.size e
204
+
let received_at e = Proto.Email.received_at e
205
+
let message_id e = Proto.Email.message_id e
206
+
let in_reply_to e = Proto.Email.in_reply_to e
207
+
let references e = Proto.Email.references e
208
+
let subject e = Proto.Email.subject e
209
+
let sent_at e = Proto.Email.sent_at e
210
+
let has_attachment e = Proto.Email.has_attachment e
211
+
let preview e = Proto.Email.preview e
212
+
213
+
(** Get active keywords as polymorphic variants. *)
214
+
let keywords e =
215
+
let kw_map = Proto.Email.keywords e in
216
+
List.filter_map (fun (k, v) ->
217
+
if v then Some (Keyword.of_string k) else None
218
+
) kw_map
219
+
220
+
(** Check if email has a specific keyword. *)
221
+
let has_keyword kw e =
222
+
let kw_str = Keyword.to_string kw in
223
+
let kw_map = Proto.Email.keywords e in
224
+
List.exists (fun (k, v) -> k = kw_str && v) kw_map
225
+
226
+
let from e = Proto.Email.from e
227
+
let to_ e = Proto.Email.to_ e
228
+
let cc e = Proto.Email.cc e
229
+
let bcc e = Proto.Email.bcc e
230
+
let reply_to e = Proto.Email.reply_to e
231
+
let sender e = Proto.Email.sender e
232
+
233
+
let text_body e = Proto.Email.text_body e
234
+
let html_body e = Proto.Email.html_body e
235
+
let attachments e = Proto.Email.attachments e
236
+
let body_values e = Proto.Email.body_values e
237
+
end
238
+
239
+
(** Email identity for sending. *)
240
+
module Identity = struct
241
+
type t = Jmap_types.identity
242
+
243
+
let id i = Proto.Identity.id i
244
+
let name i = Proto.Identity.name i
245
+
let email i = Proto.Identity.email i
246
+
let reply_to i = Proto.Identity.reply_to i
247
+
let bcc i = Proto.Identity.bcc i
248
+
let text_signature i = Proto.Identity.text_signature i
249
+
let html_signature i = Proto.Identity.html_signature i
250
+
let may_delete i = Proto.Identity.may_delete i
251
+
end
252
+
253
+
(** Email submission for outgoing mail. *)
254
+
module Submission = struct
255
+
type t = Jmap_types.submission
256
+
257
+
let id s = Proto.Submission.id s
258
+
let identity_id s = Proto.Submission.identity_id s
259
+
let email_id s = Proto.Submission.email_id s
260
+
let thread_id s = Proto.Submission.thread_id s
261
+
let send_at s = Proto.Submission.send_at s
262
+
let undo_status s = Proto.Submission.undo_status s
263
+
let delivery_status s = Proto.Submission.delivery_status s
264
+
let dsn_blob_ids s = Proto.Submission.dsn_blob_ids s
265
+
let mdn_blob_ids s = Proto.Submission.mdn_blob_ids s
266
+
end
267
+
268
+
(** Vacation auto-response. *)
269
+
module Vacation = struct
270
+
type t = Jmap_types.vacation
271
+
272
+
let id v = Proto.Vacation.id v
273
+
let is_enabled v = Proto.Vacation.is_enabled v
274
+
let from_date v = Proto.Vacation.from_date v
275
+
let to_date v = Proto.Vacation.to_date v
276
+
let subject v = Proto.Vacation.subject v
277
+
let text_body v = Proto.Vacation.text_body v
278
+
let html_body v = Proto.Vacation.html_body v
279
+
end
280
+
281
+
(** Search snippet with highlighted matches. *)
282
+
module Search_snippet = struct
283
+
type t = Jmap_types.search_snippet
284
+
285
+
let email_id s = Proto.Search_snippet.email_id s
286
+
let subject s = Proto.Search_snippet.subject s
287
+
let preview s = Proto.Search_snippet.preview s
288
+
end
289
+
290
+
(** {1 Filter Types} *)
291
+
292
+
(** Email filter conditions for queries. *)
293
+
module Email_filter = struct
294
+
type condition = Proto.Email.Filter_condition.t
295
+
296
+
(** Create an email filter condition.
297
+
298
+
All parameters are optional. Omitted parameters are not included
299
+
in the filter. Use [make ()] for an empty filter. *)
300
+
let make
301
+
?in_mailbox
302
+
?in_mailbox_other_than
303
+
?before
304
+
?after
305
+
?min_size
306
+
?max_size
307
+
?(all_in_thread_have_keyword : Keyword.t option)
308
+
?(some_in_thread_have_keyword : Keyword.t option)
309
+
?(none_in_thread_have_keyword : Keyword.t option)
310
+
?(has_keyword : Keyword.t option)
311
+
?(not_keyword : Keyword.t option)
312
+
?has_attachment
313
+
?text
314
+
?from
315
+
?to_
316
+
?cc
317
+
?bcc
318
+
?subject
319
+
?body
320
+
?header
321
+
() : condition =
322
+
{
323
+
in_mailbox;
324
+
in_mailbox_other_than;
325
+
before;
326
+
after;
327
+
min_size;
328
+
max_size;
329
+
all_in_thread_have_keyword = Option.map Keyword.to_string all_in_thread_have_keyword;
330
+
some_in_thread_have_keyword = Option.map Keyword.to_string some_in_thread_have_keyword;
331
+
none_in_thread_have_keyword = Option.map Keyword.to_string none_in_thread_have_keyword;
332
+
has_keyword = Option.map Keyword.to_string has_keyword;
333
+
not_keyword = Option.map Keyword.to_string not_keyword;
334
+
has_attachment;
335
+
text;
336
+
from;
337
+
to_;
338
+
cc;
339
+
bcc;
340
+
subject;
341
+
body;
342
+
header;
343
+
}
344
+
end
345
+
346
+
(** Mailbox filter conditions for queries. *)
347
+
module Mailbox_filter = struct
348
+
type condition = Proto.Mailbox.Filter_condition.t
349
+
350
+
let convert_role = function
351
+
| `Inbox -> Proto.Mailbox.Inbox
352
+
| `Sent -> Proto.Mailbox.Sent
353
+
| `Drafts -> Proto.Mailbox.Drafts
354
+
| `Trash -> Proto.Mailbox.Trash
355
+
| `Junk -> Proto.Mailbox.Junk
356
+
| `Archive -> Proto.Mailbox.Archive
357
+
| `Flagged -> Proto.Mailbox.Flagged
358
+
| `Important -> Proto.Mailbox.Important
359
+
| `All -> Proto.Mailbox.All
360
+
| `Subscribed -> Proto.Mailbox.Subscribed
361
+
| `Custom s -> Proto.Mailbox.Other s
362
+
363
+
(** Create a mailbox filter condition.
364
+
365
+
All parameters are optional.
366
+
For [role]: [Some (Some r)] filters by role [r], [Some None] filters for
367
+
mailboxes with no role, [None] doesn't filter by role. *)
368
+
let make
369
+
?parent_id
370
+
?name
371
+
?role
372
+
?has_any_role
373
+
?is_subscribed
374
+
() : condition =
375
+
{
376
+
parent_id;
377
+
name;
378
+
role = Option.map (Option.map convert_role) role;
379
+
has_any_role;
380
+
is_subscribed;
381
+
}
382
+
end
383
+
384
+
(** {1 Response Types} *)
385
+
386
+
(** Generic /get response wrapper. *)
387
+
module Get_response = struct
388
+
type 'a t = 'a Proto.Method.get_response
389
+
390
+
let account_id (r : 'a t) = r.Proto.Method.account_id
391
+
let state (r : 'a t) = r.Proto.Method.state
392
+
let list (r : 'a t) = r.Proto.Method.list
393
+
let not_found (r : 'a t) = r.Proto.Method.not_found
394
+
end
395
+
396
+
(** Query response. *)
397
+
module Query_response = struct
398
+
type t = Proto.Method.query_response
399
+
400
+
let account_id (r : t) = r.Proto.Method.account_id
401
+
let query_state (r : t) = r.Proto.Method.query_state
402
+
let can_calculate_changes (r : t) = r.Proto.Method.can_calculate_changes
403
+
let position (r : t) = r.Proto.Method.position
404
+
let ids (r : t) = r.Proto.Method.ids
405
+
let total (r : t) = r.Proto.Method.total
406
+
end
407
+
408
+
(** Changes response. *)
409
+
module Changes_response = struct
410
+
type t = Proto.Method.changes_response
411
+
412
+
let account_id (r : t) = r.Proto.Method.account_id
413
+
let old_state (r : t) = r.Proto.Method.old_state
414
+
let new_state (r : t) = r.Proto.Method.new_state
415
+
let has_more_changes (r : t) = r.Proto.Method.has_more_changes
416
+
let created (r : t) = r.Proto.Method.created
417
+
let updated (r : t) = r.Proto.Method.updated
418
+
let destroyed (r : t) = r.Proto.Method.destroyed
419
+
end
420
+
421
+
(** {1 JSONABLE Interface} *)
422
+
423
+
(** Module type for types that can be serialized to/from JSON bytes. *)
424
+
module type JSONABLE = sig
425
+
type t
426
+
427
+
val of_string : string -> (t, Error.t) result
428
+
val to_string : t -> (string, Error.t) result
429
+
end
430
+
431
+
(** {1 Private Interface} *)
432
+
433
+
(** Private module for internal use by Jmap_eio.
434
+
435
+
This exposes the underlying Jsont codecs for serialization. *)
436
+
module Private = struct
437
+
module Session = struct
438
+
let jsont = Proto.Session.jsont
439
+
end
440
+
441
+
module Request = struct
442
+
let jsont = Proto.Request.jsont
443
+
end
444
+
445
+
module Response = struct
446
+
let jsont = Proto.Response.jsont
447
+
end
448
+
449
+
module Mailbox = struct
450
+
let jsont = Proto.Mailbox.jsont
451
+
end
452
+
453
+
module Email = struct
454
+
let jsont = Proto.Email.jsont
455
+
end
456
+
457
+
module Thread = struct
458
+
let jsont = Proto.Thread.jsont
459
+
end
460
+
461
+
module Identity = struct
462
+
let jsont = Proto.Identity.jsont
463
+
end
464
+
465
+
module Submission = struct
466
+
let jsont = Proto.Submission.jsont
467
+
end
468
+
469
+
module Vacation = struct
470
+
let jsont = Proto.Vacation.jsont
471
+
end
472
+
473
+
module Blob = struct
474
+
let upload_response_jsont = Proto.Blob.upload_response_jsont
475
+
end
476
+
477
+
module Method = struct
478
+
let get_response_jsont = Proto.Method.get_response_jsont
479
+
let query_response_jsont = Proto.Method.query_response_jsont
480
+
let changes_response_jsont = Proto.Method.changes_response_jsont
481
+
let set_response_jsont = Proto.Method.set_response_jsont
482
+
end
483
+
484
+
module Mail_filter = struct
485
+
let email_filter_jsont = Proto.Mail_filter.email_filter_jsont
486
+
let mailbox_filter_jsont = Proto.Mail_filter.mailbox_filter_jsont
487
+
let submission_filter_jsont = Proto.Mail_filter.submission_filter_jsont
488
+
end
489
+
490
+
module Filter = struct
491
+
let comparator_jsont = Proto.Filter.comparator_jsont
492
+
end
493
+
end
+518
lib/core/jmap.mli
+518
lib/core/jmap.mli
···
1
+
(*---------------------------------------------------------------------------
2
+
Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3
+
SPDX-License-Identifier: ISC
4
+
---------------------------------------------------------------------------*)
5
+
6
+
(** Unified JMAP interface for OCaml
7
+
8
+
This module provides a clean, ergonomic API for working with JMAP
9
+
(RFC 8620/8621), combining the protocol and mail layers with abstract
10
+
types and polymorphic variants.
11
+
12
+
{2 Quick Start}
13
+
14
+
{[
15
+
open Jmap
16
+
17
+
(* Keywords use polymorphic variants *)
18
+
let is_unread email =
19
+
not (List.mem `Seen (Email.keywords email))
20
+
21
+
(* Mailbox roles are also polymorphic *)
22
+
let find_inbox mailboxes =
23
+
List.find_opt (fun m -> Mailbox.role m = Some `Inbox) mailboxes
24
+
]}
25
+
26
+
{2 Module Structure}
27
+
28
+
- {!Proto} - Low-level protocol and mail types (RFC 8620/8621)
29
+
- {!Error}, {!Id}, {!Keyword}, {!Role}, {!Capability} - Core types
30
+
- {!Session}, {!Email}, {!Mailbox}, etc. - Abstract type accessors
31
+
*)
32
+
33
+
(** {1 Protocol Layer Re-exports} *)
34
+
35
+
(** Low-level JMAP protocol types (RFC 8620/8621).
36
+
37
+
These are the raw protocol and mail types. For most use cases, prefer the
38
+
higher-level types in this module. *)
39
+
module Proto = Jmap_proto
40
+
41
+
(** {1 Core Types} *)
42
+
43
+
(** Unified error type for JMAP operations. *)
44
+
module Error : sig
45
+
(** Request-level error (RFC 7807 Problem Details). *)
46
+
type request = {
47
+
type_ : string;
48
+
status : int option;
49
+
title : string option;
50
+
detail : string option;
51
+
limit : string option;
52
+
}
53
+
54
+
(** Method-level error. *)
55
+
type method_ = {
56
+
type_ : string;
57
+
description : string option;
58
+
}
59
+
60
+
(** Set operation error for a specific object. *)
61
+
type set = {
62
+
type_ : string;
63
+
description : string option;
64
+
properties : string list option;
65
+
}
66
+
67
+
(** Unified error type.
68
+
69
+
All errors from JSON parsing, HTTP, session management, and JMAP method
70
+
calls are represented as polymorphic variants. *)
71
+
type t = [
72
+
| `Request of request
73
+
| `Method of method_
74
+
| `Set of string * set
75
+
| `Json of string
76
+
| `Http of int * string
77
+
| `Connection of string
78
+
| `Session of string
79
+
]
80
+
81
+
val pp : Format.formatter -> t -> unit
82
+
val to_string : t -> string
83
+
end
84
+
85
+
(** JMAP identifier type. *)
86
+
module Id : sig
87
+
type t
88
+
89
+
val of_string : string -> (t, string) result
90
+
val of_string_exn : string -> t
91
+
val to_string : t -> string
92
+
val compare : t -> t -> int
93
+
val equal : t -> t -> bool
94
+
val pp : Format.formatter -> t -> unit
95
+
end
96
+
97
+
(** Email keyword type.
98
+
99
+
Standard keywords are represented as polymorphic variants.
100
+
Custom keywords use [`Custom of string]. *)
101
+
module Keyword : sig
102
+
type t = [
103
+
| `Seen
104
+
| `Flagged
105
+
| `Answered
106
+
| `Draft
107
+
| `Forwarded
108
+
| `Phishing
109
+
| `Junk
110
+
| `NotJunk
111
+
| `Custom of string
112
+
]
113
+
114
+
val of_string : string -> t
115
+
val to_string : t -> string
116
+
val pp : Format.formatter -> t -> unit
117
+
end
118
+
119
+
(** Mailbox role type.
120
+
121
+
Standard roles are represented as polymorphic variants.
122
+
Custom roles use [`Custom of string]. *)
123
+
module Role : sig
124
+
type t = [
125
+
| `Inbox
126
+
| `Sent
127
+
| `Drafts
128
+
| `Trash
129
+
| `Junk
130
+
| `Archive
131
+
| `Flagged
132
+
| `Important
133
+
| `All
134
+
| `Subscribed
135
+
| `Custom of string
136
+
]
137
+
138
+
val of_string : string -> t
139
+
val to_string : t -> string
140
+
val pp : Format.formatter -> t -> unit
141
+
end
142
+
143
+
(** JMAP capability type.
144
+
145
+
Standard capabilities are represented as polymorphic variants.
146
+
Custom capabilities use [`Custom of string]. *)
147
+
module Capability : sig
148
+
type t = [
149
+
| `Core
150
+
| `Mail
151
+
| `Submission
152
+
| `VacationResponse
153
+
| `Custom of string
154
+
]
155
+
156
+
val core_uri : string
157
+
val mail_uri : string
158
+
val submission_uri : string
159
+
val vacation_uri : string
160
+
161
+
val of_string : string -> t
162
+
val to_string : t -> string
163
+
val pp : Format.formatter -> t -> unit
164
+
end
165
+
166
+
(** {1 Session Types} *)
167
+
168
+
(** JMAP session information. *)
169
+
module Session : sig
170
+
(** Account information. *)
171
+
module Account : sig
172
+
type t
173
+
174
+
val name : t -> string
175
+
val is_personal : t -> bool
176
+
val is_read_only : t -> bool
177
+
end
178
+
179
+
type t
180
+
181
+
val capabilities : t -> (string * Jsont.json) list
182
+
val accounts : t -> (Id.t * Account.t) list
183
+
val primary_accounts : t -> (string * Id.t) list
184
+
val username : t -> string
185
+
val api_url : t -> string
186
+
val download_url : t -> string
187
+
val upload_url : t -> string
188
+
val event_source_url : t -> string
189
+
val state : t -> string
190
+
191
+
val get_account : Id.t -> t -> Account.t option
192
+
val primary_account_for : string -> t -> Id.t option
193
+
val has_capability : string -> t -> bool
194
+
end
195
+
196
+
(** {1 Mail Types} *)
197
+
198
+
(** Email address with optional display name. *)
199
+
module Email_address : sig
200
+
type t
201
+
202
+
val name : t -> string option
203
+
val email : t -> string
204
+
val create : ?name:string -> string -> t
205
+
end
206
+
207
+
(** Email mailbox. *)
208
+
module Mailbox : sig
209
+
type t
210
+
211
+
val id : t -> Id.t
212
+
val name : t -> string
213
+
val parent_id : t -> Id.t option
214
+
val sort_order : t -> int64
215
+
val total_emails : t -> int64
216
+
val unread_emails : t -> int64
217
+
val total_threads : t -> int64
218
+
val unread_threads : t -> int64
219
+
val is_subscribed : t -> bool
220
+
val role : t -> Role.t option
221
+
222
+
(** Mailbox rights. *)
223
+
module Rights : sig
224
+
type t
225
+
226
+
val may_read_items : t -> bool
227
+
val may_add_items : t -> bool
228
+
val may_remove_items : t -> bool
229
+
val may_set_seen : t -> bool
230
+
val may_set_keywords : t -> bool
231
+
val may_create_child : t -> bool
232
+
val may_rename : t -> bool
233
+
val may_delete : t -> bool
234
+
val may_submit : t -> bool
235
+
end
236
+
237
+
val my_rights : t -> Rights.t
238
+
end
239
+
240
+
(** Email thread. *)
241
+
module Thread : sig
242
+
type t
243
+
244
+
val id : t -> Id.t
245
+
val email_ids : t -> Id.t list
246
+
end
247
+
248
+
(** Email message. *)
249
+
module Email : sig
250
+
(** Email body part. *)
251
+
module Body : sig
252
+
type part
253
+
type value
254
+
255
+
val part_id : part -> string option
256
+
val blob_id : part -> Id.t option
257
+
val size : part -> int64 option
258
+
val name : part -> string option
259
+
val type_ : part -> string
260
+
val charset : part -> string option
261
+
val disposition : part -> string option
262
+
val cid : part -> string option
263
+
val language : part -> string list option
264
+
val location : part -> string option
265
+
266
+
val value_text : value -> string
267
+
val value_is_truncated : value -> bool
268
+
val value_is_encoding_problem : value -> bool
269
+
end
270
+
271
+
type t
272
+
273
+
val id : t -> Id.t
274
+
val blob_id : t -> Id.t
275
+
val thread_id : t -> Id.t
276
+
val mailbox_ids : t -> (Id.t * bool) list
277
+
val size : t -> int64
278
+
val received_at : t -> Ptime.t
279
+
val message_id : t -> string list option
280
+
val in_reply_to : t -> string list option
281
+
val references : t -> string list option
282
+
val subject : t -> string option
283
+
val sent_at : t -> Ptime.t option
284
+
val has_attachment : t -> bool
285
+
val preview : t -> string
286
+
287
+
(** Get active keywords as polymorphic variants. *)
288
+
val keywords : t -> Keyword.t list
289
+
290
+
(** Check if email has a specific keyword. *)
291
+
val has_keyword : Keyword.t -> t -> bool
292
+
293
+
val from : t -> Email_address.t list option
294
+
val to_ : t -> Email_address.t list option
295
+
val cc : t -> Email_address.t list option
296
+
val bcc : t -> Email_address.t list option
297
+
val reply_to : t -> Email_address.t list option
298
+
val sender : t -> Email_address.t list option
299
+
300
+
val text_body : t -> Body.part list option
301
+
val html_body : t -> Body.part list option
302
+
val attachments : t -> Body.part list option
303
+
val body_values : t -> (string * Body.value) list option
304
+
end
305
+
306
+
(** Email identity for sending. *)
307
+
module Identity : sig
308
+
type t
309
+
310
+
val id : t -> Id.t
311
+
val name : t -> string
312
+
val email : t -> string
313
+
val reply_to : t -> Email_address.t list option
314
+
val bcc : t -> Email_address.t list option
315
+
val text_signature : t -> string
316
+
val html_signature : t -> string
317
+
val may_delete : t -> bool
318
+
end
319
+
320
+
(** Email submission for outgoing mail. *)
321
+
module Submission : sig
322
+
type t
323
+
324
+
val id : t -> Id.t
325
+
val identity_id : t -> Id.t
326
+
val email_id : t -> Id.t
327
+
val thread_id : t -> Id.t
328
+
val send_at : t -> Ptime.t
329
+
val undo_status : t -> Proto.Submission.undo_status
330
+
val delivery_status : t -> (string * Proto.Submission.Delivery_status.t) list option
331
+
val dsn_blob_ids : t -> Id.t list
332
+
val mdn_blob_ids : t -> Id.t list
333
+
end
334
+
335
+
(** Vacation auto-response. *)
336
+
module Vacation : sig
337
+
type t
338
+
339
+
val id : t -> Id.t
340
+
val is_enabled : t -> bool
341
+
val from_date : t -> Ptime.t option
342
+
val to_date : t -> Ptime.t option
343
+
val subject : t -> string option
344
+
val text_body : t -> string option
345
+
val html_body : t -> string option
346
+
end
347
+
348
+
(** Search snippet with highlighted matches. *)
349
+
module Search_snippet : sig
350
+
type t
351
+
352
+
val email_id : t -> Id.t
353
+
val subject : t -> string option
354
+
val preview : t -> string option
355
+
end
356
+
357
+
(** {1 Filter Types} *)
358
+
359
+
(** Email filter conditions for queries. *)
360
+
module Email_filter : sig
361
+
type condition
362
+
363
+
(** Create an email filter condition.
364
+
365
+
All parameters are optional. Omitted parameters are not included
366
+
in the filter. Use [make ()] for an empty filter. *)
367
+
val make :
368
+
?in_mailbox:Id.t ->
369
+
?in_mailbox_other_than:Id.t list ->
370
+
?before:Ptime.t ->
371
+
?after:Ptime.t ->
372
+
?min_size:int64 ->
373
+
?max_size:int64 ->
374
+
?all_in_thread_have_keyword:Keyword.t ->
375
+
?some_in_thread_have_keyword:Keyword.t ->
376
+
?none_in_thread_have_keyword:Keyword.t ->
377
+
?has_keyword:Keyword.t ->
378
+
?not_keyword:Keyword.t ->
379
+
?has_attachment:bool ->
380
+
?text:string ->
381
+
?from:string ->
382
+
?to_:string ->
383
+
?cc:string ->
384
+
?bcc:string ->
385
+
?subject:string ->
386
+
?body:string ->
387
+
?header:(string * string option) ->
388
+
unit -> condition
389
+
end
390
+
391
+
(** Mailbox filter conditions for queries. *)
392
+
module Mailbox_filter : sig
393
+
type condition
394
+
395
+
(** Create a mailbox filter condition.
396
+
397
+
All parameters are optional.
398
+
For [role]: [Some (Some r)] filters by role [r], [Some None] filters for
399
+
mailboxes with no role, [None] doesn't filter by role. *)
400
+
val make :
401
+
?parent_id:Id.t option ->
402
+
?name:string ->
403
+
?role:Role.t option ->
404
+
?has_any_role:bool ->
405
+
?is_subscribed:bool ->
406
+
unit -> condition
407
+
end
408
+
409
+
(** {1 Response Types} *)
410
+
411
+
(** Generic /get response wrapper. *)
412
+
module Get_response : sig
413
+
type 'a t
414
+
415
+
val account_id : 'a t -> Id.t
416
+
val state : 'a t -> string
417
+
val list : 'a t -> 'a list
418
+
val not_found : 'a t -> Id.t list
419
+
end
420
+
421
+
(** Query response. *)
422
+
module Query_response : sig
423
+
type t
424
+
425
+
val account_id : t -> Id.t
426
+
val query_state : t -> string
427
+
val can_calculate_changes : t -> bool
428
+
val position : t -> int64
429
+
val ids : t -> Id.t list
430
+
val total : t -> int64 option
431
+
end
432
+
433
+
(** Changes response. *)
434
+
module Changes_response : sig
435
+
type t
436
+
437
+
val account_id : t -> Id.t
438
+
val old_state : t -> string
439
+
val new_state : t -> string
440
+
val has_more_changes : t -> bool
441
+
val created : t -> Id.t list
442
+
val updated : t -> Id.t list
443
+
val destroyed : t -> Id.t list
444
+
end
445
+
446
+
(** {1 JSONABLE Interface} *)
447
+
448
+
(** Module type for types that can be serialized to/from JSON bytes. *)
449
+
module type JSONABLE = sig
450
+
type t
451
+
452
+
val of_string : string -> (t, Error.t) result
453
+
val to_string : t -> (string, Error.t) result
454
+
end
455
+
456
+
(** {1 Private Interface} *)
457
+
458
+
(** Private module for internal use by Jmap_eio.
459
+
460
+
This exposes the underlying Jsont codecs for serialization. *)
461
+
module Private : sig
462
+
module Session : sig
463
+
val jsont : Proto.Session.t Jsont.t
464
+
end
465
+
466
+
module Request : sig
467
+
val jsont : Proto.Request.t Jsont.t
468
+
end
469
+
470
+
module Response : sig
471
+
val jsont : Proto.Response.t Jsont.t
472
+
end
473
+
474
+
module Mailbox : sig
475
+
val jsont : Proto.Mailbox.t Jsont.t
476
+
end
477
+
478
+
module Email : sig
479
+
val jsont : Proto.Email.t Jsont.t
480
+
end
481
+
482
+
module Thread : sig
483
+
val jsont : Proto.Thread.t Jsont.t
484
+
end
485
+
486
+
module Identity : sig
487
+
val jsont : Proto.Identity.t Jsont.t
488
+
end
489
+
490
+
module Submission : sig
491
+
val jsont : Proto.Submission.t Jsont.t
492
+
end
493
+
494
+
module Vacation : sig
495
+
val jsont : Proto.Vacation.t Jsont.t
496
+
end
497
+
498
+
module Blob : sig
499
+
val upload_response_jsont : Proto.Blob.upload_response Jsont.t
500
+
end
501
+
502
+
module Method : sig
503
+
val get_response_jsont : 'a Jsont.t -> 'a Proto.Method.get_response Jsont.t
504
+
val query_response_jsont : Proto.Method.query_response Jsont.t
505
+
val changes_response_jsont : Proto.Method.changes_response Jsont.t
506
+
val set_response_jsont : 'a Jsont.t -> 'a Proto.Method.set_response Jsont.t
507
+
end
508
+
509
+
module Mail_filter : sig
510
+
val email_filter_jsont : Proto.Mail_filter.email_filter Jsont.t
511
+
val mailbox_filter_jsont : Proto.Mail_filter.mailbox_filter Jsont.t
512
+
val submission_filter_jsont : Proto.Mail_filter.submission_filter Jsont.t
513
+
end
514
+
515
+
module Filter : sig
516
+
val comparator_jsont : Proto.Filter.comparator Jsont.t
517
+
end
518
+
end
+231
lib/core/jmap_types.ml
+231
lib/core/jmap_types.ml
···
1
+
(*---------------------------------------------------------------------------
2
+
Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3
+
SPDX-License-Identifier: ISC
4
+
---------------------------------------------------------------------------*)
5
+
6
+
(** Internal types for the unified Jmap interface *)
7
+
8
+
(** {1 Error Types} *)
9
+
10
+
module Error = struct
11
+
(** Request-level error (RFC 7807 Problem Details) *)
12
+
type request = {
13
+
type_ : string;
14
+
status : int option;
15
+
title : string option;
16
+
detail : string option;
17
+
limit : string option;
18
+
}
19
+
20
+
(** Method-level error *)
21
+
type method_ = {
22
+
type_ : string;
23
+
description : string option;
24
+
}
25
+
26
+
(** Set operation error for a specific object *)
27
+
type set = {
28
+
type_ : string;
29
+
description : string option;
30
+
properties : string list option;
31
+
}
32
+
33
+
(** Unified error type *)
34
+
type t = [
35
+
| `Request of request
36
+
| `Method of method_
37
+
| `Set of string * set
38
+
| `Json of string
39
+
| `Http of int * string
40
+
| `Connection of string
41
+
| `Session of string
42
+
]
43
+
44
+
let pp_request ppf (r : request) =
45
+
Format.fprintf ppf "Request error: %s" r.type_;
46
+
Option.iter (Format.fprintf ppf " (status %d)") r.status;
47
+
Option.iter (Format.fprintf ppf ": %s") r.detail
48
+
49
+
let pp_method ppf (m : method_) =
50
+
Format.fprintf ppf "Method error: %s" m.type_;
51
+
Option.iter (Format.fprintf ppf ": %s") m.description
52
+
53
+
let pp_set ppf (id, (s : set)) =
54
+
Format.fprintf ppf "Set error for %s: %s" id s.type_;
55
+
Option.iter (Format.fprintf ppf ": %s") s.description
56
+
57
+
let pp ppf = function
58
+
| `Request r -> pp_request ppf r
59
+
| `Method m -> pp_method ppf m
60
+
| `Set (id, s) -> pp_set ppf (id, s)
61
+
| `Json msg -> Format.fprintf ppf "JSON error: %s" msg
62
+
| `Http (code, msg) -> Format.fprintf ppf "HTTP error %d: %s" code msg
63
+
| `Connection msg -> Format.fprintf ppf "Connection error: %s" msg
64
+
| `Session msg -> Format.fprintf ppf "Session error: %s" msg
65
+
66
+
let to_string e = Format.asprintf "%a" pp e
67
+
end
68
+
69
+
(** {1 Identifier Type} *)
70
+
71
+
module Id = struct
72
+
type t = Jmap_proto.Id.t
73
+
74
+
let of_string s = Jmap_proto.Id.of_string s
75
+
let of_string_exn s = Jmap_proto.Id.of_string_exn s
76
+
let to_string = Jmap_proto.Id.to_string
77
+
let compare = Jmap_proto.Id.compare
78
+
let equal = Jmap_proto.Id.equal
79
+
let pp = Jmap_proto.Id.pp
80
+
end
81
+
82
+
(** {1 Keyword Type} *)
83
+
84
+
module Keyword = struct
85
+
type t = [
86
+
| `Seen
87
+
| `Flagged
88
+
| `Answered
89
+
| `Draft
90
+
| `Forwarded
91
+
| `Phishing
92
+
| `Junk
93
+
| `NotJunk
94
+
| `Custom of string
95
+
]
96
+
97
+
let of_string = function
98
+
| "$seen" -> `Seen
99
+
| "$flagged" -> `Flagged
100
+
| "$answered" -> `Answered
101
+
| "$draft" -> `Draft
102
+
| "$forwarded" -> `Forwarded
103
+
| "$phishing" -> `Phishing
104
+
| "$junk" -> `Junk
105
+
| "$notjunk" -> `NotJunk
106
+
| s -> `Custom s
107
+
108
+
let to_string = function
109
+
| `Seen -> "$seen"
110
+
| `Flagged -> "$flagged"
111
+
| `Answered -> "$answered"
112
+
| `Draft -> "$draft"
113
+
| `Forwarded -> "$forwarded"
114
+
| `Phishing -> "$phishing"
115
+
| `Junk -> "$junk"
116
+
| `NotJunk -> "$notjunk"
117
+
| `Custom s -> s
118
+
119
+
let pp ppf k = Format.pp_print_string ppf (to_string k)
120
+
end
121
+
122
+
(** {1 Mailbox Role Type} *)
123
+
124
+
module Role = struct
125
+
type t = [
126
+
| `Inbox
127
+
| `Sent
128
+
| `Drafts
129
+
| `Trash
130
+
| `Junk
131
+
| `Archive
132
+
| `Flagged
133
+
| `Important
134
+
| `All
135
+
| `Subscribed
136
+
| `Custom of string
137
+
]
138
+
139
+
let of_string = function
140
+
| "inbox" -> `Inbox
141
+
| "sent" -> `Sent
142
+
| "drafts" -> `Drafts
143
+
| "trash" -> `Trash
144
+
| "junk" -> `Junk
145
+
| "archive" -> `Archive
146
+
| "flagged" -> `Flagged
147
+
| "important" -> `Important
148
+
| "all" -> `All
149
+
| "subscribed" -> `Subscribed
150
+
| s -> `Custom s
151
+
152
+
let to_string = function
153
+
| `Inbox -> "inbox"
154
+
| `Sent -> "sent"
155
+
| `Drafts -> "drafts"
156
+
| `Trash -> "trash"
157
+
| `Junk -> "junk"
158
+
| `Archive -> "archive"
159
+
| `Flagged -> "flagged"
160
+
| `Important -> "important"
161
+
| `All -> "all"
162
+
| `Subscribed -> "subscribed"
163
+
| `Custom s -> s
164
+
165
+
let pp ppf r = Format.pp_print_string ppf (to_string r)
166
+
end
167
+
168
+
(** {1 Capability Type} *)
169
+
170
+
module Capability = struct
171
+
type t = [
172
+
| `Core
173
+
| `Mail
174
+
| `Submission
175
+
| `VacationResponse
176
+
| `Custom of string
177
+
]
178
+
179
+
let core_uri = "urn:ietf:params:jmap:core"
180
+
let mail_uri = "urn:ietf:params:jmap:mail"
181
+
let submission_uri = "urn:ietf:params:jmap:submission"
182
+
let vacation_uri = "urn:ietf:params:jmap:vacationresponse"
183
+
184
+
let of_string = function
185
+
| s when s = core_uri -> `Core
186
+
| s when s = mail_uri -> `Mail
187
+
| s when s = submission_uri -> `Submission
188
+
| s when s = vacation_uri -> `VacationResponse
189
+
| s -> `Custom s
190
+
191
+
let to_string = function
192
+
| `Core -> core_uri
193
+
| `Mail -> mail_uri
194
+
| `Submission -> submission_uri
195
+
| `VacationResponse -> vacation_uri
196
+
| `Custom s -> s
197
+
198
+
let pp ppf c = Format.pp_print_string ppf (to_string c)
199
+
end
200
+
201
+
(** {1 Abstract Type Wrappers} *)
202
+
203
+
(** Wrapped session type *)
204
+
type session = Jmap_proto.Session.t
205
+
206
+
(** Wrapped account type *)
207
+
type account = Jmap_proto.Session.Account.t
208
+
209
+
(** Wrapped mailbox type *)
210
+
type mailbox = Jmap_proto.Mailbox.t
211
+
212
+
(** Wrapped email type *)
213
+
type email = Jmap_proto.Email.t
214
+
215
+
(** Wrapped thread type *)
216
+
type thread = Jmap_proto.Thread.t
217
+
218
+
(** Wrapped identity type *)
219
+
type identity = Jmap_proto.Identity.t
220
+
221
+
(** Wrapped email submission type *)
222
+
type submission = Jmap_proto.Submission.t
223
+
224
+
(** Wrapped vacation response type *)
225
+
type vacation = Jmap_proto.Vacation.t
226
+
227
+
(** Wrapped email address type *)
228
+
type email_address = Jmap_proto.Email_address.t
229
+
230
+
(** Wrapped search snippet type *)
231
+
type search_snippet = Jmap_proto.Search_snippet.t
+40
lib/dune
+40
lib/dune
···
1
+
(include_subdirs unqualified)
2
+
3
+
(library
4
+
(name jmap)
5
+
(public_name jmap)
6
+
(libraries jsont json-pointer ptime)
7
+
(modules
8
+
; Core unified interface
9
+
jmap
10
+
jmap_types
11
+
; Protocol layer wrapper (combines core + mail)
12
+
jmap_proto
13
+
; Core protocol modules
14
+
proto_id
15
+
proto_int53
16
+
proto_date
17
+
proto_json_map
18
+
proto_unknown
19
+
proto_error
20
+
proto_capability
21
+
proto_filter
22
+
proto_method
23
+
proto_invocation
24
+
proto_request
25
+
proto_response
26
+
proto_session
27
+
proto_push
28
+
proto_blob
29
+
; Mail modules
30
+
mail_address
31
+
mail_header
32
+
mail_body
33
+
mail_mailbox
34
+
mail_thread
35
+
mail_email
36
+
mail_snippet
37
+
mail_identity
38
+
mail_submission
39
+
mail_vacation
40
+
mail_filter))
+16
lib/mail/mail_filter.ml
+16
lib/mail/mail_filter.ml
···
1
+
(*---------------------------------------------------------------------------
2
+
Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3
+
SPDX-License-Identifier: ISC
4
+
---------------------------------------------------------------------------*)
5
+
6
+
type email_filter = Mail_email.Filter_condition.t Proto_filter.filter
7
+
8
+
let email_filter_jsont = Proto_filter.filter_jsont Mail_email.Filter_condition.jsont
9
+
10
+
type mailbox_filter = Mail_mailbox.Filter_condition.t Proto_filter.filter
11
+
12
+
let mailbox_filter_jsont = Proto_filter.filter_jsont Mail_mailbox.Filter_condition.jsont
13
+
14
+
type submission_filter = Mail_submission.Filter_condition.t Proto_filter.filter
15
+
16
+
let submission_filter_jsont = Proto_filter.filter_jsont Mail_submission.Filter_condition.jsont
+40
lib/proto/jmap_proto.ml
+40
lib/proto/jmap_proto.ml
···
1
+
(*---------------------------------------------------------------------------
2
+
Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3
+
SPDX-License-Identifier: ISC
4
+
---------------------------------------------------------------------------*)
5
+
6
+
(** JMAP Protocol Types (RFC 8620/8621)
7
+
8
+
This module re-exports all JMAP core and mail protocol types. *)
9
+
10
+
(** {1 Core Protocol Types (RFC 8620)} *)
11
+
12
+
module Id = Proto_id
13
+
module Int53 = Proto_int53
14
+
module Date = Proto_date
15
+
module Json_map = Proto_json_map
16
+
module Unknown = Proto_unknown
17
+
module Error = Proto_error
18
+
module Capability = Proto_capability
19
+
module Filter = Proto_filter
20
+
module Method = Proto_method
21
+
module Invocation = Proto_invocation
22
+
module Request = Proto_request
23
+
module Response = Proto_response
24
+
module Session = Proto_session
25
+
module Push = Proto_push
26
+
module Blob = Proto_blob
27
+
28
+
(** {1 Mail Types (RFC 8621)} *)
29
+
30
+
module Email_address = Mail_address
31
+
module Email_header = Mail_header
32
+
module Email_body = Mail_body
33
+
module Mailbox = Mail_mailbox
34
+
module Thread = Mail_thread
35
+
module Email = Mail_email
36
+
module Search_snippet = Mail_snippet
37
+
module Identity = Mail_identity
38
+
module Submission = Mail_submission
39
+
module Vacation = Mail_vacation
40
+
module Mail_filter = Mail_filter
+317
lib/proto/proto_method.ml
+317
lib/proto/proto_method.ml
···
1
+
(*---------------------------------------------------------------------------
2
+
Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3
+
SPDX-License-Identifier: ISC
4
+
---------------------------------------------------------------------------*)
5
+
6
+
(* Foo/get *)
7
+
8
+
type get_args = {
9
+
account_id : Proto_id.t;
10
+
ids : Proto_id.t list option;
11
+
properties : string list option;
12
+
}
13
+
14
+
let get_args ~account_id ?ids ?properties () =
15
+
{ account_id; ids; properties }
16
+
17
+
let get_args_make account_id ids properties =
18
+
{ account_id; ids; properties }
19
+
20
+
let get_args_jsont =
21
+
let kind = "GetArgs" in
22
+
Jsont.Object.map ~kind get_args_make
23
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun a -> a.account_id)
24
+
|> Jsont.Object.opt_mem "ids" (Jsont.list Proto_id.jsont) ~enc:(fun a -> a.ids)
25
+
|> Jsont.Object.opt_mem "properties" (Jsont.list Jsont.string) ~enc:(fun a -> a.properties)
26
+
|> Jsont.Object.finish
27
+
28
+
type 'a get_response = {
29
+
account_id : Proto_id.t;
30
+
state : string;
31
+
list : 'a list;
32
+
not_found : Proto_id.t list;
33
+
}
34
+
35
+
let get_response_jsont (type a) (obj_jsont : a Jsont.t) : a get_response Jsont.t =
36
+
let kind = "GetResponse" in
37
+
let make account_id state list not_found =
38
+
{ account_id; state; list; not_found }
39
+
in
40
+
Jsont.Object.map ~kind make
41
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun r -> r.account_id)
42
+
|> Jsont.Object.mem "state" Jsont.string ~enc:(fun r -> r.state)
43
+
|> Jsont.Object.mem "list" (Jsont.list obj_jsont) ~enc:(fun r -> r.list)
44
+
|> Jsont.Object.mem "notFound" (Jsont.list Proto_id.jsont) ~enc:(fun r -> r.not_found)
45
+
|> Jsont.Object.finish
46
+
47
+
(* Foo/changes *)
48
+
49
+
type changes_args = {
50
+
account_id : Proto_id.t;
51
+
since_state : string;
52
+
max_changes : int64 option;
53
+
}
54
+
55
+
let changes_args ~account_id ~since_state ?max_changes () =
56
+
{ account_id; since_state; max_changes }
57
+
58
+
let changes_args_make account_id since_state max_changes =
59
+
{ account_id; since_state; max_changes }
60
+
61
+
let changes_args_jsont =
62
+
let kind = "ChangesArgs" in
63
+
Jsont.Object.map ~kind changes_args_make
64
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun a -> a.account_id)
65
+
|> Jsont.Object.mem "sinceState" Jsont.string ~enc:(fun a -> a.since_state)
66
+
|> Jsont.Object.opt_mem "maxChanges" Proto_int53.Unsigned.jsont ~enc:(fun a -> a.max_changes)
67
+
|> Jsont.Object.finish
68
+
69
+
type changes_response = {
70
+
account_id : Proto_id.t;
71
+
old_state : string;
72
+
new_state : string;
73
+
has_more_changes : bool;
74
+
created : Proto_id.t list;
75
+
updated : Proto_id.t list;
76
+
destroyed : Proto_id.t list;
77
+
}
78
+
79
+
let changes_response_make account_id old_state new_state has_more_changes
80
+
created updated destroyed =
81
+
{ account_id; old_state; new_state; has_more_changes; created; updated; destroyed }
82
+
83
+
let changes_response_jsont =
84
+
let kind = "ChangesResponse" in
85
+
Jsont.Object.map ~kind changes_response_make
86
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun r -> r.account_id)
87
+
|> Jsont.Object.mem "oldState" Jsont.string ~enc:(fun r -> r.old_state)
88
+
|> Jsont.Object.mem "newState" Jsont.string ~enc:(fun r -> r.new_state)
89
+
|> Jsont.Object.mem "hasMoreChanges" Jsont.bool ~enc:(fun r -> r.has_more_changes)
90
+
|> Jsont.Object.mem "created" (Jsont.list Proto_id.jsont) ~enc:(fun r -> r.created)
91
+
|> Jsont.Object.mem "updated" (Jsont.list Proto_id.jsont) ~enc:(fun r -> r.updated)
92
+
|> Jsont.Object.mem "destroyed" (Jsont.list Proto_id.jsont) ~enc:(fun r -> r.destroyed)
93
+
|> Jsont.Object.finish
94
+
95
+
(* Foo/set *)
96
+
97
+
type 'a set_args = {
98
+
account_id : Proto_id.t;
99
+
if_in_state : string option;
100
+
create : (Proto_id.t * 'a) list option;
101
+
update : (Proto_id.t * Jsont.json) list option;
102
+
destroy : Proto_id.t list option;
103
+
}
104
+
105
+
let set_args ~account_id ?if_in_state ?create ?update ?destroy () =
106
+
{ account_id; if_in_state; create; update; destroy }
107
+
108
+
let set_args_jsont (type a) (obj_jsont : a Jsont.t) : a set_args Jsont.t =
109
+
let kind = "SetArgs" in
110
+
let make account_id if_in_state create update destroy =
111
+
{ account_id; if_in_state; create; update; destroy }
112
+
in
113
+
Jsont.Object.map ~kind make
114
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun a -> a.account_id)
115
+
|> Jsont.Object.opt_mem "ifInState" Jsont.string ~enc:(fun a -> a.if_in_state)
116
+
|> Jsont.Object.opt_mem "create" (Proto_json_map.of_id obj_jsont) ~enc:(fun a -> a.create)
117
+
|> Jsont.Object.opt_mem "update" (Proto_json_map.of_id Jsont.json) ~enc:(fun a -> a.update)
118
+
|> Jsont.Object.opt_mem "destroy" (Jsont.list Proto_id.jsont) ~enc:(fun a -> a.destroy)
119
+
|> Jsont.Object.finish
120
+
121
+
type 'a set_response = {
122
+
account_id : Proto_id.t;
123
+
old_state : string option;
124
+
new_state : string;
125
+
created : (Proto_id.t * 'a) list option;
126
+
updated : (Proto_id.t * 'a option) list option;
127
+
destroyed : Proto_id.t list option;
128
+
not_created : (Proto_id.t * Proto_error.set_error) list option;
129
+
not_updated : (Proto_id.t * Proto_error.set_error) list option;
130
+
not_destroyed : (Proto_id.t * Proto_error.set_error) list option;
131
+
}
132
+
133
+
let set_response_jsont (type a) (obj_jsont : a Jsont.t) : a set_response Jsont.t =
134
+
let kind = "SetResponse" in
135
+
let make account_id old_state new_state created updated destroyed
136
+
not_created not_updated not_destroyed =
137
+
{ account_id; old_state; new_state; created; updated; destroyed;
138
+
not_created; not_updated; not_destroyed }
139
+
in
140
+
(* For updated values, the server may return null or an object - RFC 8620 Section 5.3 *)
141
+
(* "Id[Foo|null]" means map values can be null, use Jsont.option to handle this *)
142
+
let nullable_obj = Jsont.(option obj_jsont) in
143
+
Jsont.Object.map ~kind make
144
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun r -> r.account_id)
145
+
|> Jsont.Object.opt_mem "oldState" Jsont.string ~enc:(fun r -> r.old_state)
146
+
|> Jsont.Object.mem "newState" Jsont.string ~enc:(fun r -> r.new_state)
147
+
|> Jsont.Object.opt_mem "created" (Proto_json_map.of_id obj_jsont) ~enc:(fun r -> r.created)
148
+
|> Jsont.Object.opt_mem "updated" (Proto_json_map.of_id nullable_obj) ~enc:(fun r -> r.updated)
149
+
|> Jsont.Object.opt_mem "destroyed" (Jsont.list Proto_id.jsont) ~enc:(fun r -> r.destroyed)
150
+
|> Jsont.Object.opt_mem "notCreated" (Proto_json_map.of_id Proto_error.set_error_jsont) ~enc:(fun r -> r.not_created)
151
+
|> Jsont.Object.opt_mem "notUpdated" (Proto_json_map.of_id Proto_error.set_error_jsont) ~enc:(fun r -> r.not_updated)
152
+
|> Jsont.Object.opt_mem "notDestroyed" (Proto_json_map.of_id Proto_error.set_error_jsont) ~enc:(fun r -> r.not_destroyed)
153
+
|> Jsont.Object.finish
154
+
155
+
(* Foo/copy *)
156
+
157
+
type 'a copy_args = {
158
+
from_account_id : Proto_id.t;
159
+
if_from_in_state : string option;
160
+
account_id : Proto_id.t;
161
+
if_in_state : string option;
162
+
create : (Proto_id.t * 'a) list;
163
+
on_success_destroy_original : bool;
164
+
destroy_from_if_in_state : string option;
165
+
}
166
+
167
+
let copy_args_jsont (type a) (obj_jsont : a Jsont.t) : a copy_args Jsont.t =
168
+
let kind = "CopyArgs" in
169
+
let make from_account_id if_from_in_state account_id if_in_state create
170
+
on_success_destroy_original destroy_from_if_in_state =
171
+
{ from_account_id; if_from_in_state; account_id; if_in_state; create;
172
+
on_success_destroy_original; destroy_from_if_in_state }
173
+
in
174
+
Jsont.Object.map ~kind make
175
+
|> Jsont.Object.mem "fromAccountId" Proto_id.jsont ~enc:(fun a -> a.from_account_id)
176
+
|> Jsont.Object.opt_mem "ifFromInState" Jsont.string ~enc:(fun a -> a.if_from_in_state)
177
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun a -> a.account_id)
178
+
|> Jsont.Object.opt_mem "ifInState" Jsont.string ~enc:(fun a -> a.if_in_state)
179
+
|> Jsont.Object.mem "create" (Proto_json_map.of_id obj_jsont) ~enc:(fun a -> a.create)
180
+
|> Jsont.Object.mem "onSuccessDestroyOriginal" Jsont.bool ~dec_absent:false
181
+
~enc:(fun a -> a.on_success_destroy_original)
182
+
~enc_omit:(fun b -> not b)
183
+
|> Jsont.Object.opt_mem "destroyFromIfInState" Jsont.string ~enc:(fun a -> a.destroy_from_if_in_state)
184
+
|> Jsont.Object.finish
185
+
186
+
type 'a copy_response = {
187
+
from_account_id : Proto_id.t;
188
+
account_id : Proto_id.t;
189
+
old_state : string option;
190
+
new_state : string;
191
+
created : (Proto_id.t * 'a) list option;
192
+
not_created : (Proto_id.t * Proto_error.set_error) list option;
193
+
}
194
+
195
+
let copy_response_jsont (type a) (obj_jsont : a Jsont.t) : a copy_response Jsont.t =
196
+
let kind = "CopyResponse" in
197
+
let make from_account_id account_id old_state new_state created not_created =
198
+
{ from_account_id; account_id; old_state; new_state; created; not_created }
199
+
in
200
+
Jsont.Object.map ~kind make
201
+
|> Jsont.Object.mem "fromAccountId" Proto_id.jsont ~enc:(fun r -> r.from_account_id)
202
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun r -> r.account_id)
203
+
|> Jsont.Object.opt_mem "oldState" Jsont.string ~enc:(fun r -> r.old_state)
204
+
|> Jsont.Object.mem "newState" Jsont.string ~enc:(fun r -> r.new_state)
205
+
|> Jsont.Object.opt_mem "created" (Proto_json_map.of_id obj_jsont) ~enc:(fun r -> r.created)
206
+
|> Jsont.Object.opt_mem "notCreated" (Proto_json_map.of_id Proto_error.set_error_jsont) ~enc:(fun r -> r.not_created)
207
+
|> Jsont.Object.finish
208
+
209
+
(* Foo/query *)
210
+
211
+
type 'filter query_args = {
212
+
account_id : Proto_id.t;
213
+
filter : 'filter Proto_filter.filter option;
214
+
sort : Proto_filter.comparator list option;
215
+
position : int64;
216
+
anchor : Proto_id.t option;
217
+
anchor_offset : int64;
218
+
limit : int64 option;
219
+
calculate_total : bool;
220
+
}
221
+
222
+
let query_args ~account_id ?filter ?sort ?(position = 0L) ?anchor
223
+
?(anchor_offset = 0L) ?limit ?(calculate_total = false) () =
224
+
{ account_id; filter; sort; position; anchor; anchor_offset; limit; calculate_total }
225
+
226
+
let query_args_jsont (type f) (filter_cond_jsont : f Jsont.t) : f query_args Jsont.t =
227
+
let kind = "QueryArgs" in
228
+
let make account_id filter sort position anchor anchor_offset limit calculate_total =
229
+
{ account_id; filter; sort; position; anchor; anchor_offset; limit; calculate_total }
230
+
in
231
+
Jsont.Object.map ~kind make
232
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun a -> a.account_id)
233
+
|> Jsont.Object.opt_mem "filter" (Proto_filter.filter_jsont filter_cond_jsont) ~enc:(fun a -> a.filter)
234
+
|> Jsont.Object.opt_mem "sort" (Jsont.list Proto_filter.comparator_jsont) ~enc:(fun a -> a.sort)
235
+
|> Jsont.Object.mem "position" Proto_int53.Signed.jsont ~dec_absent:0L ~enc:(fun a -> a.position)
236
+
~enc_omit:(fun p -> p = 0L)
237
+
|> Jsont.Object.opt_mem "anchor" Proto_id.jsont ~enc:(fun a -> a.anchor)
238
+
|> Jsont.Object.mem "anchorOffset" Proto_int53.Signed.jsont ~dec_absent:0L ~enc:(fun a -> a.anchor_offset)
239
+
~enc_omit:(fun o -> o = 0L)
240
+
|> Jsont.Object.opt_mem "limit" Proto_int53.Unsigned.jsont ~enc:(fun a -> a.limit)
241
+
|> Jsont.Object.mem "calculateTotal" Jsont.bool ~dec_absent:false ~enc:(fun a -> a.calculate_total)
242
+
~enc_omit:(fun b -> not b)
243
+
|> Jsont.Object.finish
244
+
245
+
type query_response = {
246
+
account_id : Proto_id.t;
247
+
query_state : string;
248
+
can_calculate_changes : bool;
249
+
position : int64;
250
+
ids : Proto_id.t list;
251
+
total : int64 option;
252
+
}
253
+
254
+
let query_response_make account_id query_state can_calculate_changes position ids total =
255
+
{ account_id; query_state; can_calculate_changes; position; ids; total }
256
+
257
+
let query_response_jsont =
258
+
let kind = "QueryResponse" in
259
+
Jsont.Object.map ~kind query_response_make
260
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun r -> r.account_id)
261
+
|> Jsont.Object.mem "queryState" Jsont.string ~enc:(fun r -> r.query_state)
262
+
|> Jsont.Object.mem "canCalculateChanges" Jsont.bool ~enc:(fun r -> r.can_calculate_changes)
263
+
|> Jsont.Object.mem "position" Proto_int53.Unsigned.jsont ~enc:(fun r -> r.position)
264
+
|> Jsont.Object.mem "ids" (Jsont.list Proto_id.jsont) ~enc:(fun r -> r.ids)
265
+
|> Jsont.Object.opt_mem "total" Proto_int53.Unsigned.jsont ~enc:(fun r -> r.total)
266
+
|> Jsont.Object.finish
267
+
268
+
(* Foo/queryChanges *)
269
+
270
+
type 'filter query_changes_args = {
271
+
account_id : Proto_id.t;
272
+
filter : 'filter Proto_filter.filter option;
273
+
sort : Proto_filter.comparator list option;
274
+
since_query_state : string;
275
+
max_changes : int64 option;
276
+
up_to_id : Proto_id.t option;
277
+
calculate_total : bool;
278
+
}
279
+
280
+
let query_changes_args_jsont (type f) (filter_cond_jsont : f Jsont.t) : f query_changes_args Jsont.t =
281
+
let kind = "QueryChangesArgs" in
282
+
let make account_id filter sort since_query_state max_changes up_to_id calculate_total =
283
+
{ account_id; filter; sort; since_query_state; max_changes; up_to_id; calculate_total }
284
+
in
285
+
Jsont.Object.map ~kind make
286
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun a -> a.account_id)
287
+
|> Jsont.Object.opt_mem "filter" (Proto_filter.filter_jsont filter_cond_jsont) ~enc:(fun a -> a.filter)
288
+
|> Jsont.Object.opt_mem "sort" (Jsont.list Proto_filter.comparator_jsont) ~enc:(fun a -> a.sort)
289
+
|> Jsont.Object.mem "sinceQueryState" Jsont.string ~enc:(fun a -> a.since_query_state)
290
+
|> Jsont.Object.opt_mem "maxChanges" Proto_int53.Unsigned.jsont ~enc:(fun a -> a.max_changes)
291
+
|> Jsont.Object.opt_mem "upToId" Proto_id.jsont ~enc:(fun a -> a.up_to_id)
292
+
|> Jsont.Object.mem "calculateTotal" Jsont.bool ~dec_absent:false ~enc:(fun a -> a.calculate_total)
293
+
~enc_omit:(fun b -> not b)
294
+
|> Jsont.Object.finish
295
+
296
+
type query_changes_response = {
297
+
account_id : Proto_id.t;
298
+
old_query_state : string;
299
+
new_query_state : string;
300
+
total : int64 option;
301
+
removed : Proto_id.t list;
302
+
added : Proto_filter.added_item list;
303
+
}
304
+
305
+
let query_changes_response_make account_id old_query_state new_query_state total removed added =
306
+
{ account_id; old_query_state; new_query_state; total; removed; added }
307
+
308
+
let query_changes_response_jsont =
309
+
let kind = "QueryChangesResponse" in
310
+
Jsont.Object.map ~kind query_changes_response_make
311
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun r -> r.account_id)
312
+
|> Jsont.Object.mem "oldQueryState" Jsont.string ~enc:(fun r -> r.old_query_state)
313
+
|> Jsont.Object.mem "newQueryState" Jsont.string ~enc:(fun r -> r.new_query_state)
314
+
|> Jsont.Object.opt_mem "total" Proto_int53.Unsigned.jsont ~enc:(fun r -> r.total)
315
+
|> Jsont.Object.mem "removed" (Jsont.list Proto_id.jsont) ~enc:(fun r -> r.removed)
316
+
|> Jsont.Object.mem "added" (Jsont.list Proto_filter.added_item_jsont) ~enc:(fun r -> r.added)
317
+
|> Jsont.Object.finish
+23
-23
proto/blob.ml
lib/proto/proto_blob.ml
+23
-23
proto/blob.ml
lib/proto/proto_blob.ml
···
4
4
---------------------------------------------------------------------------*)
5
5
6
6
type upload_response = {
7
-
account_id : Id.t;
8
-
blob_id : Id.t;
7
+
account_id : Proto_id.t;
8
+
blob_id : Proto_id.t;
9
9
type_ : string;
10
10
size : int64;
11
11
}
···
21
21
let upload_response_jsont =
22
22
let kind = "Upload response" in
23
23
Jsont.Object.map ~kind upload_response_make
24
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:upload_response_account_id
25
-
|> Jsont.Object.mem "blobId" Id.jsont ~enc:upload_response_blob_id
24
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:upload_response_account_id
25
+
|> Jsont.Object.mem "blobId" Proto_id.jsont ~enc:upload_response_blob_id
26
26
|> Jsont.Object.mem "type" Jsont.string ~enc:upload_response_type
27
-
|> Jsont.Object.mem "size" Int53.Unsigned.jsont ~enc:upload_response_size
27
+
|> Jsont.Object.mem "size" Proto_int53.Unsigned.jsont ~enc:upload_response_size
28
28
|> Jsont.Object.finish
29
29
30
30
type download_vars = {
31
-
account_id : Id.t;
32
-
blob_id : Id.t;
31
+
account_id : Proto_id.t;
32
+
blob_id : Proto_id.t;
33
33
type_ : string;
34
34
name : string;
35
35
}
···
58
58
let var = String.sub part 0 j in
59
59
let rest = String.sub part (j + 1) (String.length part - j - 1) in
60
60
let value = match var with
61
-
| "accountId" -> url_encode (Id.to_string vars.account_id)
62
-
| "blobId" -> url_encode (Id.to_string vars.blob_id)
61
+
| "accountId" -> url_encode (Proto_id.to_string vars.account_id)
62
+
| "blobId" -> url_encode (Proto_id.to_string vars.blob_id)
63
63
| "type" -> url_encode vars.type_
64
64
| "name" -> url_encode vars.name
65
65
| _ -> "{" ^ var ^ "}"
···
69
69
|> String.concat ""
70
70
71
71
type copy_args = {
72
-
from_account_id : Id.t;
73
-
account_id : Id.t;
74
-
blob_ids : Id.t list;
72
+
from_account_id : Proto_id.t;
73
+
account_id : Proto_id.t;
74
+
blob_ids : Proto_id.t list;
75
75
}
76
76
77
77
let copy_args_make from_account_id account_id blob_ids =
···
80
80
let copy_args_jsont =
81
81
let kind = "Blob/copy args" in
82
82
Jsont.Object.map ~kind copy_args_make
83
-
|> Jsont.Object.mem "fromAccountId" Id.jsont ~enc:(fun a -> a.from_account_id)
84
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun a -> a.account_id)
85
-
|> Jsont.Object.mem "blobIds" (Jsont.list Id.jsont) ~enc:(fun a -> a.blob_ids)
83
+
|> Jsont.Object.mem "fromAccountId" Proto_id.jsont ~enc:(fun a -> a.from_account_id)
84
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun a -> a.account_id)
85
+
|> Jsont.Object.mem "blobIds" (Jsont.list Proto_id.jsont) ~enc:(fun a -> a.blob_ids)
86
86
|> Jsont.Object.finish
87
87
88
88
type copy_response = {
89
-
from_account_id : Id.t;
90
-
account_id : Id.t;
91
-
copied : (Id.t * Id.t) list option;
92
-
not_copied : (Id.t * Error.set_error) list option;
89
+
from_account_id : Proto_id.t;
90
+
account_id : Proto_id.t;
91
+
copied : (Proto_id.t * Proto_id.t) list option;
92
+
not_copied : (Proto_id.t * Proto_error.set_error) list option;
93
93
}
94
94
95
95
let copy_response_make from_account_id account_id copied not_copied =
···
98
98
let copy_response_jsont =
99
99
let kind = "Blob/copy response" in
100
100
Jsont.Object.map ~kind copy_response_make
101
-
|> Jsont.Object.mem "fromAccountId" Id.jsont ~enc:(fun r -> r.from_account_id)
102
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun r -> r.account_id)
103
-
|> Jsont.Object.opt_mem "copied" (Json_map.of_id Id.jsont) ~enc:(fun r -> r.copied)
104
-
|> Jsont.Object.opt_mem "notCopied" (Json_map.of_id Error.set_error_jsont) ~enc:(fun r -> r.not_copied)
101
+
|> Jsont.Object.mem "fromAccountId" Proto_id.jsont ~enc:(fun r -> r.from_account_id)
102
+
|> Jsont.Object.mem "accountId" Proto_id.jsont ~enc:(fun r -> r.account_id)
103
+
|> Jsont.Object.opt_mem "copied" (Proto_json_map.of_id Proto_id.jsont) ~enc:(fun r -> r.copied)
104
+
|> Jsont.Object.opt_mem "notCopied" (Proto_json_map.of_id Proto_error.set_error_jsont) ~enc:(fun r -> r.not_copied)
105
105
|> Jsont.Object.finish
+16
-14
proto/blob.mli
lib/proto/proto_blob.mli
+16
-14
proto/blob.mli
lib/proto/proto_blob.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** JMAP blob upload/download types as defined in RFC 8620 Section 6 *)
6
+
(** JMAP blob upload/download types as defined in RFC 8620 Section 6
7
+
8
+
@canonical Jmap.Proto.Blob *)
7
9
8
10
(** {1 Upload Response} *)
9
11
10
12
(** Response from a blob upload. *)
11
13
type upload_response = {
12
-
account_id : Id.t;
14
+
account_id : Proto_id.t;
13
15
(** The account the blob was uploaded to. *)
14
-
blob_id : Id.t;
16
+
blob_id : Proto_id.t;
15
17
(** The server-assigned blob id. *)
16
18
type_ : string;
17
19
(** The media type of the uploaded blob. *)
···
19
21
(** The size in octets. *)
20
22
}
21
23
22
-
val upload_response_account_id : upload_response -> Id.t
23
-
val upload_response_blob_id : upload_response -> Id.t
24
+
val upload_response_account_id : upload_response -> Proto_id.t
25
+
val upload_response_blob_id : upload_response -> Proto_id.t
24
26
val upload_response_type : upload_response -> string
25
27
val upload_response_size : upload_response -> int64
26
28
···
30
32
31
33
(** Variables for the download URL template. *)
32
34
type download_vars = {
33
-
account_id : Id.t;
34
-
blob_id : Id.t;
35
+
account_id : Proto_id.t;
36
+
blob_id : Proto_id.t;
35
37
type_ : string;
36
38
name : string;
37
39
}
···
45
47
46
48
(** Arguments for Blob/copy. *)
47
49
type copy_args = {
48
-
from_account_id : Id.t;
49
-
account_id : Id.t;
50
-
blob_ids : Id.t list;
50
+
from_account_id : Proto_id.t;
51
+
account_id : Proto_id.t;
52
+
blob_ids : Proto_id.t list;
51
53
}
52
54
53
55
val copy_args_jsont : copy_args Jsont.t
54
56
55
57
(** Response for Blob/copy. *)
56
58
type copy_response = {
57
-
from_account_id : Id.t;
58
-
account_id : Id.t;
59
-
copied : (Id.t * Id.t) list option;
59
+
from_account_id : Proto_id.t;
60
+
account_id : Proto_id.t;
61
+
copied : (Proto_id.t * Proto_id.t) list option;
60
62
(** Map of old blob id to new blob id. *)
61
-
not_copied : (Id.t * Error.set_error) list option;
63
+
not_copied : (Proto_id.t * Proto_error.set_error) list option;
62
64
(** Blobs that could not be copied. *)
63
65
}
64
66
+8
-8
proto/capability.ml
lib/proto/proto_capability.ml
+8
-8
proto/capability.ml
lib/proto/proto_capability.ml
···
46
46
let jsont =
47
47
let kind = "Core capability" in
48
48
Jsont.Object.map ~kind make
49
-
|> Jsont.Object.mem "maxSizeUpload" Int53.Unsigned.jsont ~enc:max_size_upload
49
+
|> Jsont.Object.mem "maxSizeUpload" Proto_int53.Unsigned.jsont ~enc:max_size_upload
50
50
|> Jsont.Object.mem "maxConcurrentUpload" Jsont.int ~enc:max_concurrent_upload
51
-
|> Jsont.Object.mem "maxSizeRequest" Int53.Unsigned.jsont ~enc:max_size_request
51
+
|> Jsont.Object.mem "maxSizeRequest" Proto_int53.Unsigned.jsont ~enc:max_size_request
52
52
|> Jsont.Object.mem "maxConcurrentRequests" Jsont.int ~enc:max_concurrent_requests
53
53
|> Jsont.Object.mem "maxCallsInRequest" Jsont.int ~enc:max_calls_in_request
54
54
|> Jsont.Object.mem "maxObjectsInGet" Jsont.int ~enc:max_objects_in_get
···
91
91
let jsont =
92
92
let kind = "Mail capability" in
93
93
Jsont.Object.map ~kind make
94
-
|> Jsont.Object.opt_mem "maxMailboxesPerEmail" Int53.Unsigned.jsont ~enc:max_mailboxes_per_email
95
-
|> Jsont.Object.opt_mem "maxMailboxDepth" Int53.Unsigned.jsont ~enc:max_mailbox_depth
96
-
|> Jsont.Object.mem "maxSizeMailboxName" Int53.Unsigned.jsont ~enc:max_size_mailbox_name
97
-
|> Jsont.Object.mem "maxSizeAttachmentsPerEmail" Int53.Unsigned.jsont ~enc:max_size_attachments_per_email
94
+
|> Jsont.Object.opt_mem "maxMailboxesPerEmail" Proto_int53.Unsigned.jsont ~enc:max_mailboxes_per_email
95
+
|> Jsont.Object.opt_mem "maxMailboxDepth" Proto_int53.Unsigned.jsont ~enc:max_mailbox_depth
96
+
|> Jsont.Object.mem "maxSizeMailboxName" Proto_int53.Unsigned.jsont ~enc:max_size_mailbox_name
97
+
|> Jsont.Object.mem "maxSizeAttachmentsPerEmail" Proto_int53.Unsigned.jsont ~enc:max_size_attachments_per_email
98
98
|> Jsont.Object.mem "emailQuerySortOptions" (Jsont.list Jsont.string) ~enc:email_query_sort_options
99
99
|> Jsont.Object.mem "mayCreateTopLevelMailbox" Jsont.bool ~enc:may_create_top_level_mailbox
100
100
|> Jsont.Object.finish
···
116
116
{ max_delayed_send; submission_extensions }
117
117
118
118
let submission_extensions_jsont =
119
-
Json_map.of_string (Jsont.list Jsont.string)
119
+
Proto_json_map.of_string (Jsont.list Jsont.string)
120
120
121
121
let jsont =
122
122
let kind = "Submission capability" in
123
123
Jsont.Object.map ~kind make
124
-
|> Jsont.Object.mem "maxDelayedSend" Int53.Unsigned.jsont ~enc:max_delayed_send
124
+
|> Jsont.Object.mem "maxDelayedSend" Proto_int53.Unsigned.jsont ~enc:max_delayed_send
125
125
|> Jsont.Object.mem "submissionExtensions" submission_extensions_jsont ~enc:submission_extensions
126
126
|> Jsont.Object.finish
127
127
end
+3
-1
proto/capability.mli
lib/proto/proto_capability.mli
+3
-1
proto/capability.mli
lib/proto/proto_capability.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** JMAP capability types as defined in RFC 8620 Section 2 *)
6
+
(** JMAP capability types as defined in RFC 8620 Section 2
7
+
8
+
@canonical Jmap.Proto.Capability *)
7
9
8
10
(** {1 Standard Capability URIs} *)
9
11
proto/date.ml
lib/proto/proto_date.ml
proto/date.ml
lib/proto/proto_date.ml
+3
-1
proto/date.mli
lib/proto/proto_date.mli
+3
-1
proto/date.mli
lib/proto/proto_date.mli
···
7
7
8
8
JMAP uses RFC 3339 formatted date-time strings.
9
9
10
-
See {{:https://datatracker.ietf.org/doc/html/rfc8620#section-1.4} RFC 8620 Section 1.4}. *)
10
+
See {{:https://datatracker.ietf.org/doc/html/rfc8620#section-1.4} RFC 8620 Section 1.4}.
11
+
12
+
@canonical Jmap.Proto.Date *)
11
13
12
14
(** RFC 3339 date-time.
13
15
-21
proto/dune
-21
proto/dune
···
1
-
(library
2
-
(name jmap_proto)
3
-
(public_name jmap)
4
-
(libraries jsont json-pointer ptime)
5
-
(modules
6
-
jmap_proto
7
-
id
8
-
int53
9
-
date
10
-
json_map
11
-
unknown
12
-
error
13
-
capability
14
-
filter
15
-
method_
16
-
invocation
17
-
request
18
-
response
19
-
session
20
-
push
21
-
blob))
proto/error.ml
lib/proto/proto_error.ml
proto/error.ml
lib/proto/proto_error.ml
+3
-1
proto/error.mli
lib/proto/proto_error.mli
+3
-1
proto/error.mli
lib/proto/proto_error.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** JMAP error types as defined in RFC 8620 Section 3.6.1-3.6.2 *)
6
+
(** JMAP error types as defined in RFC 8620 Section 3.6.1-3.6.2
7
+
8
+
@canonical Jmap.Proto.Error *)
7
9
8
10
(** {1 Request-Level Errors}
9
11
+3
-3
proto/filter.ml
lib/proto/proto_filter.ml
+3
-3
proto/filter.ml
lib/proto/proto_filter.ml
···
107
107
|> Jsont.Object.finish
108
108
109
109
type added_item = {
110
-
id : Id.t;
110
+
id : Proto_id.t;
111
111
index : int64;
112
112
}
113
113
···
118
118
let added_item_jsont =
119
119
let kind = "AddedItem" in
120
120
Jsont.Object.map ~kind added_item_make
121
-
|> Jsont.Object.mem "id" Id.jsont ~enc:added_item_id
122
-
|> Jsont.Object.mem "index" Int53.Unsigned.jsont ~enc:added_item_index
121
+
|> Jsont.Object.mem "id" Proto_id.jsont ~enc:added_item_id
122
+
|> Jsont.Object.mem "index" Proto_int53.Unsigned.jsont ~enc:added_item_index
123
123
|> Jsont.Object.finish
+4
-2
proto/filter.mli
lib/proto/proto_filter.mli
+4
-2
proto/filter.mli
lib/proto/proto_filter.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** JMAP filter and sort types as defined in RFC 8620 Section 5.5 *)
6
+
(** JMAP filter and sort types as defined in RFC 8620 Section 5.5
7
+
8
+
@canonical Jmap.Proto.Filter *)
7
9
8
10
(** {1 Filter Operators} *)
9
11
···
66
68
67
69
(** Added entry position in query change results. *)
68
70
type added_item = {
69
-
id : Id.t;
71
+
id : Proto_id.t;
70
72
index : int64;
71
73
}
72
74
proto/id.ml
lib/proto/proto_id.ml
proto/id.ml
lib/proto/proto_id.ml
+3
-1
proto/id.mli
lib/proto/proto_id.mli
+3
-1
proto/id.mli
lib/proto/proto_id.mli
···
8
8
An Id is a string of 1-255 octets from the URL-safe base64 alphabet
9
9
(A-Za-z0-9_-), plus the ASCII alphanumeric characters.
10
10
11
-
See {{:https://datatracker.ietf.org/doc/html/rfc8620#section-1.2} RFC 8620 Section 1.2}. *)
11
+
See {{:https://datatracker.ietf.org/doc/html/rfc8620#section-1.2} RFC 8620 Section 1.2}.
12
+
13
+
@canonical Jmap.Proto.Id *)
12
14
13
15
type t
14
16
(** The type of JMAP identifiers. *)
proto/int53.ml
lib/proto/proto_int53.ml
proto/int53.ml
lib/proto/proto_int53.ml
+3
-1
proto/int53.mli
lib/proto/proto_int53.mli
+3
-1
proto/int53.mli
lib/proto/proto_int53.mli
···
9
9
IEEE 754 double-precision floating point format without loss of precision.
10
10
The safe range is -2^53+1 to 2^53-1.
11
11
12
-
See {{:https://datatracker.ietf.org/doc/html/rfc8620#section-1.3} RFC 8620 Section 1.3}. *)
12
+
See {{:https://datatracker.ietf.org/doc/html/rfc8620#section-1.3} RFC 8620 Section 1.3}.
13
+
14
+
@canonical Jmap.Proto.Int53 *)
13
15
14
16
(** 53-bit signed integer.
15
17
+4
-4
proto/invocation.ml
lib/proto/proto_invocation.ml
+4
-4
proto/invocation.ml
lib/proto/proto_invocation.ml
···
100
100
Jsont.map ~kind ~dec ~enc Jsont.json
101
101
102
102
let make_get ~method_call_id ~method_name args =
103
-
let arguments = encode_json_value Method_.get_args_jsont args in
103
+
let arguments = encode_json_value Proto_method.get_args_jsont args in
104
104
{ name = method_name; arguments; method_call_id }
105
105
106
106
let make_changes ~method_call_id ~method_name args =
107
-
let arguments = encode_json_value Method_.changes_args_jsont args in
107
+
let arguments = encode_json_value Proto_method.changes_args_jsont args in
108
108
{ name = method_name; arguments; method_call_id }
109
109
110
110
let make_query (type f) ~method_call_id ~method_name
111
-
~(filter_cond_jsont : f Jsont.t) (args : f Method_.query_args) =
112
-
let arguments = encode_json_value (Method_.query_args_jsont filter_cond_jsont) args in
111
+
~(filter_cond_jsont : f Jsont.t) (args : f Proto_method.query_args) =
112
+
let arguments = encode_json_value (Proto_method.query_args_jsont filter_cond_jsont) args in
113
113
{ name = method_name; arguments; method_call_id }
+6
-4
proto/invocation.mli
lib/proto/proto_invocation.mli
+6
-4
proto/invocation.mli
lib/proto/proto_invocation.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** JMAP method invocation as defined in RFC 8620 Section 3.2 *)
6
+
(** JMAP method invocation as defined in RFC 8620 Section 3.2
7
+
8
+
@canonical Jmap.Proto.Invocation *)
7
9
8
10
(** {1 Result References} *)
9
11
···
103
105
val make_get :
104
106
method_call_id:string ->
105
107
method_name:string ->
106
-
Method_.get_args ->
108
+
Proto_method.get_args ->
107
109
t
108
110
(** [make_get ~method_call_id ~method_name args] creates a /get invocation. *)
109
111
110
112
val make_changes :
111
113
method_call_id:string ->
112
114
method_name:string ->
113
-
Method_.changes_args ->
115
+
Proto_method.changes_args ->
114
116
t
115
117
(** [make_changes ~method_call_id ~method_name args] creates a /changes invocation. *)
116
118
···
118
120
method_call_id:string ->
119
121
method_name:string ->
120
122
filter_cond_jsont:'f Jsont.t ->
121
-
'f Method_.query_args ->
123
+
'f Proto_method.query_args ->
122
124
t
123
125
(** [make_query ~method_call_id ~method_name ~filter_cond_jsont args] creates a /query invocation. *)
-24
proto/jmap_proto.ml
-24
proto/jmap_proto.ml
···
1
-
(*---------------------------------------------------------------------------
2
-
Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3
-
SPDX-License-Identifier: ISC
4
-
---------------------------------------------------------------------------*)
5
-
6
-
(** JMAP Protocol Types (RFC 8620)
7
-
8
-
This module re-exports all JMAP core protocol types. *)
9
-
10
-
module Id = Id
11
-
module Int53 = Int53
12
-
module Date = Date
13
-
module Json_map = Json_map
14
-
module Unknown = Unknown
15
-
module Error = Error
16
-
module Capability = Capability
17
-
module Filter = Filter
18
-
module Method = Method_
19
-
module Invocation = Invocation
20
-
module Request = Request
21
-
module Response = Response
22
-
module Session = Session
23
-
module Push = Push
24
-
module Blob = Blob
+2
-2
proto/json_map.ml
lib/proto/proto_json_map.ml
+2
-2
proto/json_map.ml
lib/proto/proto_json_map.ml
···
25
25
let string_codec = of_string value_jsont in
26
26
let dec pairs =
27
27
List.map (fun (k, v) ->
28
-
match Id.of_string k with
28
+
match Proto_id.of_string k with
29
29
| Ok id -> (id, v)
30
30
| Error msg -> Jsont.Error.msgf Jsont.Meta.none "%s: invalid key %s - %s" kind k msg
31
31
) pairs
32
32
in
33
33
let enc pairs =
34
-
List.map (fun (id, v) -> (Id.to_string id, v)) pairs
34
+
List.map (fun (id, v) -> (Proto_id.to_string id, v)) pairs
35
35
in
36
36
Jsont.map ~kind ~dec ~enc string_codec
37
37
+5
-3
proto/json_map.mli
lib/proto/proto_json_map.mli
+5
-3
proto/json_map.mli
lib/proto/proto_json_map.mli
···
6
6
(** JSON object-as-map codec utilities.
7
7
8
8
JMAP frequently uses JSON objects as maps with string or Id keys.
9
-
These codecs convert between JSON objects and OCaml association lists. *)
9
+
These codecs convert between JSON objects and OCaml association lists.
10
+
11
+
@canonical Jmap.Proto.Json_map *)
10
12
11
13
val of_string : 'a Jsont.t -> (string * 'a) list Jsont.t
12
14
(** [of_string value_jsont] creates a codec for JSON objects
13
15
used as string-keyed maps. Returns an association list. *)
14
16
15
-
val of_id : 'a Jsont.t -> (Id.t * 'a) list Jsont.t
17
+
val of_id : 'a Jsont.t -> (Proto_id.t * 'a) list Jsont.t
16
18
(** [of_id value_jsont] creates a codec for JSON objects
17
19
keyed by JMAP identifiers. *)
18
20
19
-
val id_to_bool : (Id.t * bool) list Jsont.t
21
+
val id_to_bool : (Proto_id.t * bool) list Jsont.t
20
22
(** Codec for Id[Boolean] maps, common in JMAP (e.g., mailboxIds, keywords). *)
21
23
22
24
val string_to_bool : (string * bool) list Jsont.t
-17
proto/mail/dune
-17
proto/mail/dune
+45
-45
proto/mail/email.ml
lib/mail/mail_email.ml
+45
-45
proto/mail/email.ml
lib/mail/mail_email.ml
···
15
15
end
16
16
17
17
type t = {
18
-
id : Jmap_proto.Id.t;
19
-
blob_id : Jmap_proto.Id.t;
20
-
thread_id : Jmap_proto.Id.t;
18
+
id : Proto_id.t;
19
+
blob_id : Proto_id.t;
20
+
thread_id : Proto_id.t;
21
21
size : int64;
22
22
received_at : Ptime.t;
23
-
mailbox_ids : (Jmap_proto.Id.t * bool) list;
23
+
mailbox_ids : (Proto_id.t * bool) list;
24
24
keywords : (string * bool) list;
25
25
message_id : string list option;
26
26
in_reply_to : string list option;
27
27
references : string list option;
28
-
sender : Email_address.t list option;
29
-
from : Email_address.t list option;
30
-
to_ : Email_address.t list option;
31
-
cc : Email_address.t list option;
32
-
bcc : Email_address.t list option;
33
-
reply_to : Email_address.t list option;
28
+
sender : Mail_address.t list option;
29
+
from : Mail_address.t list option;
30
+
to_ : Mail_address.t list option;
31
+
cc : Mail_address.t list option;
32
+
bcc : Mail_address.t list option;
33
+
reply_to : Mail_address.t list option;
34
34
subject : string option;
35
35
sent_at : Ptime.t option;
36
-
headers : Email_header.t list option;
37
-
body_structure : Email_body.Part.t option;
38
-
body_values : (string * Email_body.Value.t) list option;
39
-
text_body : Email_body.Part.t list option;
40
-
html_body : Email_body.Part.t list option;
41
-
attachments : Email_body.Part.t list option;
36
+
headers : Mail_header.t list option;
37
+
body_structure : Mail_body.Part.t option;
38
+
body_values : (string * Mail_body.Value.t) list option;
39
+
text_body : Mail_body.Part.t list option;
40
+
html_body : Mail_body.Part.t list option;
41
+
attachments : Mail_body.Part.t list option;
42
42
has_attachment : bool;
43
43
preview : string;
44
44
}
···
81
81
82
82
let jsont =
83
83
let kind = "Email" in
84
-
let body_values_jsont = Jmap_proto.Json_map.of_string Email_body.Value.jsont in
84
+
let body_values_jsont = Proto_json_map.of_string Mail_body.Value.jsont in
85
85
(* subject can be null per RFC 8621 Section 4.1.1 *)
86
86
let nullable_string = Jsont.(option string) in
87
87
Jsont.Object.map ~kind make
88
-
|> Jsont.Object.mem "id" Jmap_proto.Id.jsont ~enc:id
89
-
|> Jsont.Object.mem "blobId" Jmap_proto.Id.jsont ~enc:blob_id
90
-
|> Jsont.Object.mem "threadId" Jmap_proto.Id.jsont ~enc:thread_id
91
-
|> Jsont.Object.mem "size" Jmap_proto.Int53.Unsigned.jsont ~enc:size
92
-
|> Jsont.Object.mem "receivedAt" Jmap_proto.Date.Utc.jsont ~enc:received_at
93
-
|> Jsont.Object.mem "mailboxIds" Jmap_proto.Json_map.id_to_bool ~enc:mailbox_ids
94
-
|> Jsont.Object.mem "keywords" Jmap_proto.Json_map.string_to_bool ~dec_absent:[] ~enc:keywords
88
+
|> Jsont.Object.mem "id" Proto_id.jsont ~enc:id
89
+
|> Jsont.Object.mem "blobId" Proto_id.jsont ~enc:blob_id
90
+
|> Jsont.Object.mem "threadId" Proto_id.jsont ~enc:thread_id
91
+
|> Jsont.Object.mem "size" Proto_int53.Unsigned.jsont ~enc:size
92
+
|> Jsont.Object.mem "receivedAt" Proto_date.Utc.jsont ~enc:received_at
93
+
|> Jsont.Object.mem "mailboxIds" Proto_json_map.id_to_bool ~enc:mailbox_ids
94
+
|> Jsont.Object.mem "keywords" Proto_json_map.string_to_bool ~dec_absent:[] ~enc:keywords
95
95
(* Header fields can be absent or null per RFC 8621 *)
96
96
|> Jsont.Object.mem "messageId" Jsont.(option (list string))
97
97
~dec_absent:None ~enc_omit:Option.is_none ~enc:message_id
···
99
99
~dec_absent:None ~enc_omit:Option.is_none ~enc:in_reply_to
100
100
|> Jsont.Object.mem "references" Jsont.(option (list string))
101
101
~dec_absent:None ~enc_omit:Option.is_none ~enc:references
102
-
|> Jsont.Object.mem "sender" Jsont.(option (list Email_address.jsont))
102
+
|> Jsont.Object.mem "sender" Jsont.(option (list Mail_address.jsont))
103
103
~dec_absent:None ~enc_omit:Option.is_none ~enc:sender
104
-
|> Jsont.Object.mem "from" Jsont.(option (list Email_address.jsont))
104
+
|> Jsont.Object.mem "from" Jsont.(option (list Mail_address.jsont))
105
105
~dec_absent:None ~enc_omit:Option.is_none ~enc:from
106
-
|> Jsont.Object.mem "to" Jsont.(option (list Email_address.jsont))
106
+
|> Jsont.Object.mem "to" Jsont.(option (list Mail_address.jsont))
107
107
~dec_absent:None ~enc_omit:Option.is_none ~enc:to_
108
-
|> Jsont.Object.mem "cc" Jsont.(option (list Email_address.jsont))
108
+
|> Jsont.Object.mem "cc" Jsont.(option (list Mail_address.jsont))
109
109
~dec_absent:None ~enc_omit:Option.is_none ~enc:cc
110
-
|> Jsont.Object.mem "bcc" Jsont.(option (list Email_address.jsont))
110
+
|> Jsont.Object.mem "bcc" Jsont.(option (list Mail_address.jsont))
111
111
~dec_absent:None ~enc_omit:Option.is_none ~enc:bcc
112
-
|> Jsont.Object.mem "replyTo" Jsont.(option (list Email_address.jsont))
112
+
|> Jsont.Object.mem "replyTo" Jsont.(option (list Mail_address.jsont))
113
113
~dec_absent:None ~enc_omit:Option.is_none ~enc:reply_to
114
114
|> Jsont.Object.mem "subject" nullable_string
115
115
~dec_absent:None ~enc_omit:Option.is_none ~enc:subject
116
-
|> Jsont.Object.opt_mem "sentAt" Jmap_proto.Date.Rfc3339.jsont ~enc:sent_at
117
-
|> Jsont.Object.opt_mem "headers" (Jsont.list Email_header.jsont) ~enc:headers
118
-
|> Jsont.Object.opt_mem "bodyStructure" Email_body.Part.jsont ~enc:body_structure
116
+
|> Jsont.Object.opt_mem "sentAt" Proto_date.Rfc3339.jsont ~enc:sent_at
117
+
|> Jsont.Object.opt_mem "headers" (Jsont.list Mail_header.jsont) ~enc:headers
118
+
|> Jsont.Object.opt_mem "bodyStructure" Mail_body.Part.jsont ~enc:body_structure
119
119
|> Jsont.Object.opt_mem "bodyValues" body_values_jsont ~enc:body_values
120
-
|> Jsont.Object.opt_mem "textBody" (Jsont.list Email_body.Part.jsont) ~enc:text_body
121
-
|> Jsont.Object.opt_mem "htmlBody" (Jsont.list Email_body.Part.jsont) ~enc:html_body
122
-
|> Jsont.Object.opt_mem "attachments" (Jsont.list Email_body.Part.jsont) ~enc:attachments
120
+
|> Jsont.Object.opt_mem "textBody" (Jsont.list Mail_body.Part.jsont) ~enc:text_body
121
+
|> Jsont.Object.opt_mem "htmlBody" (Jsont.list Mail_body.Part.jsont) ~enc:html_body
122
+
|> Jsont.Object.opt_mem "attachments" (Jsont.list Mail_body.Part.jsont) ~enc:attachments
123
123
|> Jsont.Object.mem "hasAttachment" Jsont.bool ~dec_absent:false ~enc:has_attachment
124
124
|> Jsont.Object.mem "preview" Jsont.string ~dec_absent:"" ~enc:preview
125
125
|> Jsont.Object.finish
126
126
127
127
module Filter_condition = struct
128
128
type t = {
129
-
in_mailbox : Jmap_proto.Id.t option;
130
-
in_mailbox_other_than : Jmap_proto.Id.t list option;
129
+
in_mailbox : Proto_id.t option;
130
+
in_mailbox_other_than : Proto_id.t list option;
131
131
before : Ptime.t option;
132
132
after : Ptime.t option;
133
133
min_size : int64 option;
···
179
179
let jsont =
180
180
let kind = "EmailFilterCondition" in
181
181
Jsont.Object.map ~kind make
182
-
|> Jsont.Object.opt_mem "inMailbox" Jmap_proto.Id.jsont ~enc:(fun f -> f.in_mailbox)
183
-
|> Jsont.Object.opt_mem "inMailboxOtherThan" (Jsont.list Jmap_proto.Id.jsont) ~enc:(fun f -> f.in_mailbox_other_than)
184
-
|> Jsont.Object.opt_mem "before" Jmap_proto.Date.Utc.jsont ~enc:(fun f -> f.before)
185
-
|> Jsont.Object.opt_mem "after" Jmap_proto.Date.Utc.jsont ~enc:(fun f -> f.after)
186
-
|> Jsont.Object.opt_mem "minSize" Jmap_proto.Int53.Unsigned.jsont ~enc:(fun f -> f.min_size)
187
-
|> Jsont.Object.opt_mem "maxSize" Jmap_proto.Int53.Unsigned.jsont ~enc:(fun f -> f.max_size)
182
+
|> Jsont.Object.opt_mem "inMailbox" Proto_id.jsont ~enc:(fun f -> f.in_mailbox)
183
+
|> Jsont.Object.opt_mem "inMailboxOtherThan" (Jsont.list Proto_id.jsont) ~enc:(fun f -> f.in_mailbox_other_than)
184
+
|> Jsont.Object.opt_mem "before" Proto_date.Utc.jsont ~enc:(fun f -> f.before)
185
+
|> Jsont.Object.opt_mem "after" Proto_date.Utc.jsont ~enc:(fun f -> f.after)
186
+
|> Jsont.Object.opt_mem "minSize" Proto_int53.Unsigned.jsont ~enc:(fun f -> f.min_size)
187
+
|> Jsont.Object.opt_mem "maxSize" Proto_int53.Unsigned.jsont ~enc:(fun f -> f.max_size)
188
188
|> Jsont.Object.opt_mem "allInThreadHaveKeyword" Jsont.string ~enc:(fun f -> f.all_in_thread_have_keyword)
189
189
|> Jsont.Object.opt_mem "someInThreadHaveKeyword" Jsont.string ~enc:(fun f -> f.some_in_thread_have_keyword)
190
190
|> Jsont.Object.opt_mem "noneInThreadHaveKeyword" Jsont.string ~enc:(fun f -> f.none_in_thread_have_keyword)
···
225
225
~enc:(fun a -> a.fetch_html_body_values) ~enc_omit:(fun b -> not b)
226
226
|> Jsont.Object.mem "fetchAllBodyValues" Jsont.bool ~dec_absent:false
227
227
~enc:(fun a -> a.fetch_all_body_values) ~enc_omit:(fun b -> not b)
228
-
|> Jsont.Object.opt_mem "maxBodyValueBytes" Jmap_proto.Int53.Unsigned.jsont ~enc:(fun a -> a.max_body_value_bytes)
228
+
|> Jsont.Object.opt_mem "maxBodyValueBytes" Proto_int53.Unsigned.jsont ~enc:(fun a -> a.max_body_value_bytes)
229
229
|> Jsont.Object.finish
+37
-35
proto/mail/email.mli
lib/mail/mail_email.mli
+37
-35
proto/mail/email.mli
lib/mail/mail_email.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** Email type as defined in RFC 8621 Section 4 *)
6
+
(** Email type as defined in RFC 8621 Section 4
7
+
8
+
@canonical Jmap.Proto.Email *)
7
9
8
10
(** {1 Standard Keywords} *)
9
11
···
38
40
39
41
type t = {
40
42
(* Metadata - server-set, immutable *)
41
-
id : Jmap_proto.Id.t;
42
-
blob_id : Jmap_proto.Id.t;
43
-
thread_id : Jmap_proto.Id.t;
43
+
id : Proto_id.t;
44
+
blob_id : Proto_id.t;
45
+
thread_id : Proto_id.t;
44
46
size : int64;
45
47
received_at : Ptime.t;
46
48
47
49
(* Metadata - mutable *)
48
-
mailbox_ids : (Jmap_proto.Id.t * bool) list;
50
+
mailbox_ids : (Proto_id.t * bool) list;
49
51
keywords : (string * bool) list;
50
52
51
53
(* Parsed headers *)
52
54
message_id : string list option;
53
55
in_reply_to : string list option;
54
56
references : string list option;
55
-
sender : Email_address.t list option;
56
-
from : Email_address.t list option;
57
-
to_ : Email_address.t list option;
58
-
cc : Email_address.t list option;
59
-
bcc : Email_address.t list option;
60
-
reply_to : Email_address.t list option;
57
+
sender : Mail_address.t list option;
58
+
from : Mail_address.t list option;
59
+
to_ : Mail_address.t list option;
60
+
cc : Mail_address.t list option;
61
+
bcc : Mail_address.t list option;
62
+
reply_to : Mail_address.t list option;
61
63
subject : string option;
62
64
sent_at : Ptime.t option;
63
65
64
66
(* Raw headers *)
65
-
headers : Email_header.t list option;
67
+
headers : Mail_header.t list option;
66
68
67
69
(* Body structure *)
68
-
body_structure : Email_body.Part.t option;
69
-
body_values : (string * Email_body.Value.t) list option;
70
-
text_body : Email_body.Part.t list option;
71
-
html_body : Email_body.Part.t list option;
72
-
attachments : Email_body.Part.t list option;
70
+
body_structure : Mail_body.Part.t option;
71
+
body_values : (string * Mail_body.Value.t) list option;
72
+
text_body : Mail_body.Part.t list option;
73
+
html_body : Mail_body.Part.t list option;
74
+
attachments : Mail_body.Part.t list option;
73
75
has_attachment : bool;
74
76
preview : string;
75
77
}
76
78
77
-
val id : t -> Jmap_proto.Id.t
78
-
val blob_id : t -> Jmap_proto.Id.t
79
-
val thread_id : t -> Jmap_proto.Id.t
79
+
val id : t -> Proto_id.t
80
+
val blob_id : t -> Proto_id.t
81
+
val thread_id : t -> Proto_id.t
80
82
val size : t -> int64
81
83
val received_at : t -> Ptime.t
82
-
val mailbox_ids : t -> (Jmap_proto.Id.t * bool) list
84
+
val mailbox_ids : t -> (Proto_id.t * bool) list
83
85
val keywords : t -> (string * bool) list
84
86
val message_id : t -> string list option
85
87
val in_reply_to : t -> string list option
86
88
val references : t -> string list option
87
-
val sender : t -> Email_address.t list option
88
-
val from : t -> Email_address.t list option
89
-
val to_ : t -> Email_address.t list option
90
-
val cc : t -> Email_address.t list option
91
-
val bcc : t -> Email_address.t list option
92
-
val reply_to : t -> Email_address.t list option
89
+
val sender : t -> Mail_address.t list option
90
+
val from : t -> Mail_address.t list option
91
+
val to_ : t -> Mail_address.t list option
92
+
val cc : t -> Mail_address.t list option
93
+
val bcc : t -> Mail_address.t list option
94
+
val reply_to : t -> Mail_address.t list option
93
95
val subject : t -> string option
94
96
val sent_at : t -> Ptime.t option
95
-
val headers : t -> Email_header.t list option
96
-
val body_structure : t -> Email_body.Part.t option
97
-
val body_values : t -> (string * Email_body.Value.t) list option
98
-
val text_body : t -> Email_body.Part.t list option
99
-
val html_body : t -> Email_body.Part.t list option
100
-
val attachments : t -> Email_body.Part.t list option
97
+
val headers : t -> Mail_header.t list option
98
+
val body_structure : t -> Mail_body.Part.t option
99
+
val body_values : t -> (string * Mail_body.Value.t) list option
100
+
val text_body : t -> Mail_body.Part.t list option
101
+
val html_body : t -> Mail_body.Part.t list option
102
+
val attachments : t -> Mail_body.Part.t list option
101
103
val has_attachment : t -> bool
102
104
val preview : t -> string
103
105
···
107
109
108
110
module Filter_condition : sig
109
111
type t = {
110
-
in_mailbox : Jmap_proto.Id.t option;
111
-
in_mailbox_other_than : Jmap_proto.Id.t list option;
112
+
in_mailbox : Proto_id.t option;
113
+
in_mailbox_other_than : Proto_id.t list option;
112
114
before : Ptime.t option;
113
115
after : Ptime.t option;
114
116
min_size : int64 option;
proto/mail/email_address.ml
lib/mail/mail_address.ml
proto/mail/email_address.ml
lib/mail/mail_address.ml
+3
-1
proto/mail/email_address.mli
lib/mail/mail_address.mli
+3
-1
proto/mail/email_address.mli
lib/mail/mail_address.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** Email address types as defined in RFC 8621 Section 4.1.2.3 *)
6
+
(** Email address types as defined in RFC 8621 Section 4.1.2.3
7
+
8
+
@canonical Jmap.Proto.Email_address *)
7
9
8
10
(** {1 Email Address} *)
9
11
+5
-5
proto/mail/email_body.ml
lib/mail/mail_body.ml
+5
-5
proto/mail/email_body.ml
lib/mail/mail_body.ml
···
31
31
module Part = struct
32
32
type t = {
33
33
part_id : string option;
34
-
blob_id : Jmap_proto.Id.t option;
34
+
blob_id : Proto_id.t option;
35
35
size : int64 option;
36
-
headers : Email_header.t list option;
36
+
headers : Mail_header.t list option;
37
37
name : string option;
38
38
type_ : string;
39
39
charset : string option;
···
66
66
in
67
67
(* Many fields can be null per RFC 8621 Section 4.1.4 *)
68
68
let nullable_string = Jsont.(option string) in
69
-
let nullable_id = Jsont.(option Jmap_proto.Id.jsont) in
69
+
let nullable_id = Jsont.(option Proto_id.jsont) in
70
70
lazy (
71
71
Jsont.Object.map ~kind make
72
72
|> Jsont.Object.mem "partId" nullable_string
73
73
~dec_absent:None ~enc_omit:Option.is_none ~enc:part_id
74
74
|> Jsont.Object.mem "blobId" nullable_id
75
75
~dec_absent:None ~enc_omit:Option.is_none ~enc:blob_id
76
-
|> Jsont.Object.opt_mem "size" Jmap_proto.Int53.Unsigned.jsont ~enc:size
77
-
|> Jsont.Object.opt_mem "headers" (Jsont.list Email_header.jsont) ~enc:headers
76
+
|> Jsont.Object.opt_mem "size" Proto_int53.Unsigned.jsont ~enc:size
77
+
|> Jsont.Object.opt_mem "headers" (Jsont.list Mail_header.jsont) ~enc:headers
78
78
|> Jsont.Object.mem "name" nullable_string
79
79
~dec_absent:None ~enc_omit:Option.is_none ~enc:name
80
80
|> Jsont.Object.mem "type" Jsont.string ~enc:type_
+7
-5
proto/mail/email_body.mli
lib/mail/mail_body.mli
+7
-5
proto/mail/email_body.mli
lib/mail/mail_body.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** Email body types as defined in RFC 8621 Section 4.1.4 *)
6
+
(** Email body types as defined in RFC 8621 Section 4.1.4
7
+
8
+
@canonical Jmap.Proto.Email_body *)
7
9
8
10
(** {1 Body Value} *)
9
11
···
32
34
type t = {
33
35
part_id : string option;
34
36
(** Identifier for this part, used to fetch content. *)
35
-
blob_id : Jmap_proto.Id.t option;
37
+
blob_id : Proto_id.t option;
36
38
(** Blob id if the part can be fetched as a blob. *)
37
39
size : int64 option;
38
40
(** Size in octets. *)
39
-
headers : Email_header.t list option;
41
+
headers : Mail_header.t list option;
40
42
(** Headers specific to this part. *)
41
43
name : string option;
42
44
(** Suggested filename from Content-Disposition. *)
···
57
59
}
58
60
59
61
val part_id : t -> string option
60
-
val blob_id : t -> Jmap_proto.Id.t option
62
+
val blob_id : t -> Proto_id.t option
61
63
val size : t -> int64 option
62
-
val headers : t -> Email_header.t list option
64
+
val headers : t -> Mail_header.t list option
63
65
val name : t -> string option
64
66
val type_ : t -> string
65
67
val charset : t -> string option
+3
-3
proto/mail/email_header.ml
lib/mail/mail_header.ml
+3
-3
proto/mail/email_header.ml
lib/mail/mail_header.ml
···
28
28
29
29
let text_jsont = Jsont.string
30
30
31
-
let addresses_jsont = Jsont.list Email_address.jsont
31
+
let addresses_jsont = Jsont.list Mail_address.jsont
32
32
33
-
let grouped_addresses_jsont = Jsont.list Email_address.Group.jsont
33
+
let grouped_addresses_jsont = Jsont.list Mail_address.Group.jsont
34
34
35
35
let message_ids_jsont = Jsont.list Jsont.string
36
36
37
-
let date_jsont = Jmap_proto.Date.Rfc3339.jsont
37
+
let date_jsont = Proto_date.Rfc3339.jsont
38
38
39
39
let urls_jsont = Jsont.list Jsont.string
+5
-3
proto/mail/email_header.mli
lib/mail/mail_header.mli
+5
-3
proto/mail/email_header.mli
lib/mail/mail_header.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** Email header types as defined in RFC 8621 Section 4.1.2 *)
6
+
(** Email header types as defined in RFC 8621 Section 4.1.2
7
+
8
+
@canonical Jmap.Proto.Email_header *)
7
9
8
10
(** {1 Raw Headers} *)
9
11
···
34
36
val text_jsont : string Jsont.t
35
37
36
38
(** The addresses form - list of email addresses. *)
37
-
val addresses_jsont : Email_address.t list Jsont.t
39
+
val addresses_jsont : Mail_address.t list Jsont.t
38
40
39
41
(** The grouped addresses form - addresses with group info. *)
40
-
val grouped_addresses_jsont : Email_address.Group.t list Jsont.t
42
+
val grouped_addresses_jsont : Mail_address.Group.t list Jsont.t
41
43
42
44
(** The message IDs form - list of message-id strings. *)
43
45
val message_ids_jsont : string list Jsont.t
+6
-6
proto/mail/identity.ml
lib/mail/mail_identity.ml
+6
-6
proto/mail/identity.ml
lib/mail/mail_identity.ml
···
4
4
---------------------------------------------------------------------------*)
5
5
6
6
type t = {
7
-
id : Jmap_proto.Id.t;
7
+
id : Proto_id.t;
8
8
name : string;
9
9
email : string;
10
-
reply_to : Email_address.t list option;
11
-
bcc : Email_address.t list option;
10
+
reply_to : Mail_address.t list option;
11
+
bcc : Mail_address.t list option;
12
12
text_signature : string;
13
13
html_signature : string;
14
14
may_delete : bool;
···
29
29
let jsont =
30
30
let kind = "Identity" in
31
31
Jsont.Object.map ~kind make
32
-
|> Jsont.Object.mem "id" Jmap_proto.Id.jsont ~enc:id
32
+
|> Jsont.Object.mem "id" Proto_id.jsont ~enc:id
33
33
|> Jsont.Object.mem "name" Jsont.string ~dec_absent:"" ~enc:name
34
34
|> Jsont.Object.mem "email" Jsont.string ~enc:email
35
-
|> Jsont.Object.opt_mem "replyTo" (Jsont.list Email_address.jsont) ~enc:reply_to
36
-
|> Jsont.Object.opt_mem "bcc" (Jsont.list Email_address.jsont) ~enc:bcc
35
+
|> Jsont.Object.opt_mem "replyTo" (Jsont.list Mail_address.jsont) ~enc:reply_to
36
+
|> Jsont.Object.opt_mem "bcc" (Jsont.list Mail_address.jsont) ~enc:bcc
37
37
|> Jsont.Object.mem "textSignature" Jsont.string ~dec_absent:"" ~enc:text_signature
38
38
|> Jsont.Object.mem "htmlSignature" Jsont.string ~dec_absent:"" ~enc:html_signature
39
39
|> Jsont.Object.mem "mayDelete" Jsont.bool ~enc:may_delete
+9
-7
proto/mail/identity.mli
lib/mail/mail_identity.mli
+9
-7
proto/mail/identity.mli
lib/mail/mail_identity.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** Identity type as defined in RFC 8621 Section 6 *)
6
+
(** Identity type as defined in RFC 8621 Section 6
7
+
8
+
@canonical Jmap.Proto.Identity *)
7
9
8
10
type t = {
9
-
id : Jmap_proto.Id.t;
11
+
id : Proto_id.t;
10
12
(** Server-assigned identity id. *)
11
13
name : string;
12
14
(** Display name for sent emails. *)
13
15
email : string;
14
16
(** The email address to use. *)
15
-
reply_to : Email_address.t list option;
17
+
reply_to : Mail_address.t list option;
16
18
(** Default Reply-To addresses. *)
17
-
bcc : Email_address.t list option;
19
+
bcc : Mail_address.t list option;
18
20
(** Default BCC addresses. *)
19
21
text_signature : string;
20
22
(** Plain text signature. *)
···
24
26
(** Whether the user may delete this identity. *)
25
27
}
26
28
27
-
val id : t -> Jmap_proto.Id.t
29
+
val id : t -> Proto_id.t
28
30
val name : t -> string
29
31
val email : t -> string
30
-
val reply_to : t -> Email_address.t list option
31
-
val bcc : t -> Email_address.t list option
32
+
val reply_to : t -> Mail_address.t list option
33
+
val bcc : t -> Mail_address.t list option
32
34
val text_signature : t -> string
33
35
val html_signature : t -> string
34
36
val may_delete : t -> bool
-20
proto/mail/jmap_mail.ml
-20
proto/mail/jmap_mail.ml
···
1
-
(*---------------------------------------------------------------------------
2
-
Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3
-
SPDX-License-Identifier: ISC
4
-
---------------------------------------------------------------------------*)
5
-
6
-
(** JMAP Mail Types (RFC 8621)
7
-
8
-
This module re-exports all JMAP mail protocol types. *)
9
-
10
-
module Email_address = Email_address
11
-
module Email_header = Email_header
12
-
module Email_body = Email_body
13
-
module Mailbox = Mailbox
14
-
module Thread = Thread
15
-
module Email = Email
16
-
module Search_snippet = Search_snippet
17
-
module Identity = Identity
18
-
module Submission = Submission
19
-
module Vacation = Vacation
20
-
module Mail_filter = Mail_filter
-16
proto/mail/mail_filter.ml
-16
proto/mail/mail_filter.ml
···
1
-
(*---------------------------------------------------------------------------
2
-
Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3
-
SPDX-License-Identifier: ISC
4
-
---------------------------------------------------------------------------*)
5
-
6
-
type email_filter = Email.Filter_condition.t Jmap_proto.Filter.filter
7
-
8
-
let email_filter_jsont = Jmap_proto.Filter.filter_jsont Email.Filter_condition.jsont
9
-
10
-
type mailbox_filter = Mailbox.Filter_condition.t Jmap_proto.Filter.filter
11
-
12
-
let mailbox_filter_jsont = Jmap_proto.Filter.filter_jsont Mailbox.Filter_condition.jsont
13
-
14
-
type submission_filter = Submission.Filter_condition.t Jmap_proto.Filter.filter
15
-
16
-
let submission_filter_jsont = Jmap_proto.Filter.filter_jsont Submission.Filter_condition.jsont
+6
-4
proto/mail/mail_filter.mli
lib/mail/mail_filter.mli
+6
-4
proto/mail/mail_filter.mli
lib/mail/mail_filter.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** Mail-specific filter types *)
6
+
(** Mail-specific filter types
7
+
8
+
@canonical Jmap.Proto.Mail_filter *)
7
9
8
10
(** Email filter with Email-specific conditions. *)
9
-
type email_filter = Email.Filter_condition.t Jmap_proto.Filter.filter
11
+
type email_filter = Mail_email.Filter_condition.t Proto_filter.filter
10
12
11
13
val email_filter_jsont : email_filter Jsont.t
12
14
13
15
(** Mailbox filter with Mailbox-specific conditions. *)
14
-
type mailbox_filter = Mailbox.Filter_condition.t Jmap_proto.Filter.filter
16
+
type mailbox_filter = Mail_mailbox.Filter_condition.t Proto_filter.filter
15
17
16
18
val mailbox_filter_jsont : mailbox_filter Jsont.t
17
19
18
20
(** EmailSubmission filter with Submission-specific conditions. *)
19
-
type submission_filter = Submission.Filter_condition.t Jmap_proto.Filter.filter
21
+
type submission_filter = Mail_submission.Filter_condition.t Proto_filter.filter
20
22
21
23
val submission_filter_jsont : submission_filter Jsont.t
+11
-11
proto/mail/mailbox.ml
lib/mail/mail_mailbox.ml
+11
-11
proto/mail/mailbox.ml
lib/mail/mail_mailbox.ml
···
92
92
Jsont.string
93
93
94
94
type t = {
95
-
id : Jmap_proto.Id.t;
95
+
id : Proto_id.t;
96
96
name : string;
97
-
parent_id : Jmap_proto.Id.t option;
97
+
parent_id : Proto_id.t option;
98
98
role : role option;
99
99
sort_order : int64;
100
100
total_emails : int64;
···
125
125
let jsont =
126
126
let kind = "Mailbox" in
127
127
(* parentId and role can be null - RFC 8621 Section 2 *)
128
-
let nullable_id = Jsont.(option Jmap_proto.Id.jsont) in
128
+
let nullable_id = Jsont.(option Proto_id.jsont) in
129
129
let nullable_role = Jsont.(option role_jsont) in
130
130
Jsont.Object.map ~kind make
131
-
|> Jsont.Object.mem "id" Jmap_proto.Id.jsont ~enc:id
131
+
|> Jsont.Object.mem "id" Proto_id.jsont ~enc:id
132
132
|> Jsont.Object.mem "name" Jsont.string ~enc:name
133
133
|> Jsont.Object.mem "parentId" nullable_id
134
134
~dec_absent:None ~enc_omit:Option.is_none ~enc:parent_id
135
135
|> Jsont.Object.mem "role" nullable_role
136
136
~dec_absent:None ~enc_omit:Option.is_none ~enc:role
137
-
|> Jsont.Object.mem "sortOrder" Jmap_proto.Int53.Unsigned.jsont ~dec_absent:0L ~enc:sort_order
138
-
|> Jsont.Object.mem "totalEmails" Jmap_proto.Int53.Unsigned.jsont ~enc:total_emails
139
-
|> Jsont.Object.mem "unreadEmails" Jmap_proto.Int53.Unsigned.jsont ~enc:unread_emails
140
-
|> Jsont.Object.mem "totalThreads" Jmap_proto.Int53.Unsigned.jsont ~enc:total_threads
141
-
|> Jsont.Object.mem "unreadThreads" Jmap_proto.Int53.Unsigned.jsont ~enc:unread_threads
137
+
|> Jsont.Object.mem "sortOrder" Proto_int53.Unsigned.jsont ~dec_absent:0L ~enc:sort_order
138
+
|> Jsont.Object.mem "totalEmails" Proto_int53.Unsigned.jsont ~enc:total_emails
139
+
|> Jsont.Object.mem "unreadEmails" Proto_int53.Unsigned.jsont ~enc:unread_emails
140
+
|> Jsont.Object.mem "totalThreads" Proto_int53.Unsigned.jsont ~enc:total_threads
141
+
|> Jsont.Object.mem "unreadThreads" Proto_int53.Unsigned.jsont ~enc:unread_threads
142
142
|> Jsont.Object.mem "myRights" Rights.jsont ~enc:my_rights
143
143
|> Jsont.Object.mem "isSubscribed" Jsont.bool ~enc:is_subscribed
144
144
|> Jsont.Object.finish
145
145
146
146
module Filter_condition = struct
147
147
type t = {
148
-
parent_id : Jmap_proto.Id.t option option;
148
+
parent_id : Proto_id.t option option;
149
149
name : string option;
150
150
role : role option option;
151
151
has_any_role : bool option;
···
162
162
- None = field absent (don't filter)
163
163
- Some None = field present with null (filter for no parent/role)
164
164
- Some (Some x) = field present with value (filter for specific value) *)
165
-
let nullable_id = Jsont.(option Jmap_proto.Id.jsont) in
165
+
let nullable_id = Jsont.(option Proto_id.jsont) in
166
166
let nullable_role = Jsont.(option role_jsont) in
167
167
Jsont.Object.map ~kind make
168
168
|> Jsont.Object.opt_mem "parentId" nullable_id ~enc:(fun f -> f.parent_id)
+8
-6
proto/mail/mailbox.mli
lib/mail/mail_mailbox.mli
+8
-6
proto/mail/mailbox.mli
lib/mail/mail_mailbox.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** Mailbox type as defined in RFC 8621 Section 2 *)
6
+
(** Mailbox type as defined in RFC 8621 Section 2
7
+
8
+
@canonical Jmap.Proto.Mailbox *)
7
9
8
10
(** {1 Mailbox Rights} *)
9
11
···
57
59
(** {1 Mailbox} *)
58
60
59
61
type t = {
60
-
id : Jmap_proto.Id.t;
62
+
id : Proto_id.t;
61
63
(** Server-assigned mailbox id. *)
62
64
name : string;
63
65
(** User-visible name (UTF-8). *)
64
-
parent_id : Jmap_proto.Id.t option;
66
+
parent_id : Proto_id.t option;
65
67
(** Id of parent mailbox, or [None] for root. *)
66
68
role : role option;
67
69
(** Standard role, if any. *)
···
81
83
(** Whether user is subscribed to this mailbox. *)
82
84
}
83
85
84
-
val id : t -> Jmap_proto.Id.t
86
+
val id : t -> Proto_id.t
85
87
val name : t -> string
86
-
val parent_id : t -> Jmap_proto.Id.t option
88
+
val parent_id : t -> Proto_id.t option
87
89
val role : t -> role option
88
90
val sort_order : t -> int64
89
91
val total_emails : t -> int64
···
100
102
(** Filter conditions for Mailbox/query. *)
101
103
module Filter_condition : sig
102
104
type t = {
103
-
parent_id : Jmap_proto.Id.t option option;
105
+
parent_id : Proto_id.t option option;
104
106
(** Filter by parent. [Some None] = top-level only. *)
105
107
name : string option;
106
108
(** Filter by exact name match. *)
+2
-2
proto/mail/search_snippet.ml
lib/mail/mail_snippet.ml
+2
-2
proto/mail/search_snippet.ml
lib/mail/mail_snippet.ml
···
4
4
---------------------------------------------------------------------------*)
5
5
6
6
type t = {
7
-
email_id : Jmap_proto.Id.t;
7
+
email_id : Proto_id.t;
8
8
subject : string option;
9
9
preview : string option;
10
10
}
···
20
20
(* subject and preview can be null per RFC 8621 Section 5 *)
21
21
let nullable_string = Jsont.(option string) in
22
22
Jsont.Object.map ~kind make
23
-
|> Jsont.Object.mem "emailId" Jmap_proto.Id.jsont ~enc:email_id
23
+
|> Jsont.Object.mem "emailId" Proto_id.jsont ~enc:email_id
24
24
|> Jsont.Object.mem "subject" nullable_string
25
25
~dec_absent:None ~enc_omit:Option.is_none ~enc:subject
26
26
|> Jsont.Object.mem "preview" nullable_string
+5
-3
proto/mail/search_snippet.mli
lib/mail/mail_snippet.mli
+5
-3
proto/mail/search_snippet.mli
lib/mail/mail_snippet.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** SearchSnippet type as defined in RFC 8621 Section 5 *)
6
+
(** SearchSnippet type as defined in RFC 8621 Section 5
7
+
8
+
@canonical Jmap.Proto.Search_snippet *)
7
9
8
10
type t = {
9
-
email_id : Jmap_proto.Id.t;
11
+
email_id : Proto_id.t;
10
12
(** The email this snippet is for. *)
11
13
subject : string option;
12
14
(** HTML snippet of matching subject text. *)
···
14
16
(** HTML snippet of matching body text. *)
15
17
}
16
18
17
-
val email_id : t -> Jmap_proto.Id.t
19
+
val email_id : t -> Proto_id.t
18
20
val subject : t -> string option
19
21
val preview : t -> string option
20
22
+23
-23
proto/mail/submission.ml
lib/mail/mail_submission.ml
+23
-23
proto/mail/submission.ml
lib/mail/mail_submission.ml
···
18
18
let kind = "EmailSubmission Address" in
19
19
Jsont.Object.map ~kind make
20
20
|> Jsont.Object.mem "email" Jsont.string ~enc:email
21
-
|> Jsont.Object.opt_mem "parameters" (Jmap_proto.Json_map.of_string Jsont.string) ~enc:parameters
21
+
|> Jsont.Object.opt_mem "parameters" (Proto_json_map.of_string Jsont.string) ~enc:parameters
22
22
|> Jsont.Object.finish
23
23
end
24
24
···
114
114
~dec:undo_status_of_string ~enc:undo_status_to_string Jsont.string
115
115
116
116
type t = {
117
-
id : Jmap_proto.Id.t;
118
-
identity_id : Jmap_proto.Id.t;
119
-
email_id : Jmap_proto.Id.t;
120
-
thread_id : Jmap_proto.Id.t;
117
+
id : Proto_id.t;
118
+
identity_id : Proto_id.t;
119
+
email_id : Proto_id.t;
120
+
thread_id : Proto_id.t;
121
121
envelope : Envelope.t option;
122
122
send_at : Ptime.t;
123
123
undo_status : undo_status;
124
124
delivery_status : (string * Delivery_status.t) list option;
125
-
dsn_blob_ids : Jmap_proto.Id.t list;
126
-
mdn_blob_ids : Jmap_proto.Id.t list;
125
+
dsn_blob_ids : Proto_id.t list;
126
+
mdn_blob_ids : Proto_id.t list;
127
127
}
128
128
129
129
let id t = t.id
···
145
145
let jsont =
146
146
let kind = "EmailSubmission" in
147
147
Jsont.Object.map ~kind make
148
-
|> Jsont.Object.mem "id" Jmap_proto.Id.jsont ~enc:id
149
-
|> Jsont.Object.mem "identityId" Jmap_proto.Id.jsont ~enc:identity_id
150
-
|> Jsont.Object.mem "emailId" Jmap_proto.Id.jsont ~enc:email_id
151
-
|> Jsont.Object.mem "threadId" Jmap_proto.Id.jsont ~enc:thread_id
148
+
|> Jsont.Object.mem "id" Proto_id.jsont ~enc:id
149
+
|> Jsont.Object.mem "identityId" Proto_id.jsont ~enc:identity_id
150
+
|> Jsont.Object.mem "emailId" Proto_id.jsont ~enc:email_id
151
+
|> Jsont.Object.mem "threadId" Proto_id.jsont ~enc:thread_id
152
152
|> Jsont.Object.opt_mem "envelope" Envelope.jsont ~enc:envelope
153
-
|> Jsont.Object.mem "sendAt" Jmap_proto.Date.Utc.jsont ~enc:send_at
153
+
|> Jsont.Object.mem "sendAt" Proto_date.Utc.jsont ~enc:send_at
154
154
|> Jsont.Object.mem "undoStatus" undo_status_jsont ~enc:undo_status
155
-
|> Jsont.Object.opt_mem "deliveryStatus" (Jmap_proto.Json_map.of_string Delivery_status.jsont) ~enc:delivery_status
156
-
|> Jsont.Object.mem "dsnBlobIds" (Jsont.list Jmap_proto.Id.jsont) ~dec_absent:[] ~enc:dsn_blob_ids
157
-
|> Jsont.Object.mem "mdnBlobIds" (Jsont.list Jmap_proto.Id.jsont) ~dec_absent:[] ~enc:mdn_blob_ids
155
+
|> Jsont.Object.opt_mem "deliveryStatus" (Proto_json_map.of_string Delivery_status.jsont) ~enc:delivery_status
156
+
|> Jsont.Object.mem "dsnBlobIds" (Jsont.list Proto_id.jsont) ~dec_absent:[] ~enc:dsn_blob_ids
157
+
|> Jsont.Object.mem "mdnBlobIds" (Jsont.list Proto_id.jsont) ~dec_absent:[] ~enc:mdn_blob_ids
158
158
|> Jsont.Object.finish
159
159
160
160
module Filter_condition = struct
161
161
type t = {
162
-
identity_ids : Jmap_proto.Id.t list option;
163
-
email_ids : Jmap_proto.Id.t list option;
164
-
thread_ids : Jmap_proto.Id.t list option;
162
+
identity_ids : Proto_id.t list option;
163
+
email_ids : Proto_id.t list option;
164
+
thread_ids : Proto_id.t list option;
165
165
undo_status : undo_status option;
166
166
before : Ptime.t option;
167
167
after : Ptime.t option;
···
173
173
let jsont =
174
174
let kind = "EmailSubmissionFilterCondition" in
175
175
Jsont.Object.map ~kind make
176
-
|> Jsont.Object.opt_mem "identityIds" (Jsont.list Jmap_proto.Id.jsont) ~enc:(fun f -> f.identity_ids)
177
-
|> Jsont.Object.opt_mem "emailIds" (Jsont.list Jmap_proto.Id.jsont) ~enc:(fun f -> f.email_ids)
178
-
|> Jsont.Object.opt_mem "threadIds" (Jsont.list Jmap_proto.Id.jsont) ~enc:(fun f -> f.thread_ids)
176
+
|> Jsont.Object.opt_mem "identityIds" (Jsont.list Proto_id.jsont) ~enc:(fun f -> f.identity_ids)
177
+
|> Jsont.Object.opt_mem "emailIds" (Jsont.list Proto_id.jsont) ~enc:(fun f -> f.email_ids)
178
+
|> Jsont.Object.opt_mem "threadIds" (Jsont.list Proto_id.jsont) ~enc:(fun f -> f.thread_ids)
179
179
|> Jsont.Object.opt_mem "undoStatus" undo_status_jsont ~enc:(fun f -> f.undo_status)
180
-
|> Jsont.Object.opt_mem "before" Jmap_proto.Date.Utc.jsont ~enc:(fun f -> f.before)
181
-
|> Jsont.Object.opt_mem "after" Jmap_proto.Date.Utc.jsont ~enc:(fun f -> f.after)
180
+
|> Jsont.Object.opt_mem "before" Proto_date.Utc.jsont ~enc:(fun f -> f.before)
181
+
|> Jsont.Object.opt_mem "after" Proto_date.Utc.jsont ~enc:(fun f -> f.after)
182
182
|> Jsont.Object.finish
183
183
end
+18
-16
proto/mail/submission.mli
lib/mail/mail_submission.mli
+18
-16
proto/mail/submission.mli
lib/mail/mail_submission.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** EmailSubmission type as defined in RFC 8621 Section 7 *)
6
+
(** EmailSubmission type as defined in RFC 8621 Section 7
7
+
8
+
@canonical Jmap.Proto.Submission *)
7
9
8
10
(** {1 Address} *)
9
11
···
81
83
(** {1 EmailSubmission} *)
82
84
83
85
type t = {
84
-
id : Jmap_proto.Id.t;
86
+
id : Proto_id.t;
85
87
(** Server-assigned submission id. *)
86
-
identity_id : Jmap_proto.Id.t;
88
+
identity_id : Proto_id.t;
87
89
(** The identity used to send. *)
88
-
email_id : Jmap_proto.Id.t;
90
+
email_id : Proto_id.t;
89
91
(** The email that was submitted. *)
90
-
thread_id : Jmap_proto.Id.t;
92
+
thread_id : Proto_id.t;
91
93
(** The thread of the submitted email. *)
92
94
envelope : Envelope.t option;
93
95
(** The envelope used, if different from email headers. *)
···
97
99
(** Whether sending can be undone. *)
98
100
delivery_status : (string * Delivery_status.t) list option;
99
101
(** Delivery status per recipient. *)
100
-
dsn_blob_ids : Jmap_proto.Id.t list;
102
+
dsn_blob_ids : Proto_id.t list;
101
103
(** Blob ids of received DSN messages. *)
102
-
mdn_blob_ids : Jmap_proto.Id.t list;
104
+
mdn_blob_ids : Proto_id.t list;
103
105
(** Blob ids of received MDN messages. *)
104
106
}
105
107
106
-
val id : t -> Jmap_proto.Id.t
107
-
val identity_id : t -> Jmap_proto.Id.t
108
-
val email_id : t -> Jmap_proto.Id.t
109
-
val thread_id : t -> Jmap_proto.Id.t
108
+
val id : t -> Proto_id.t
109
+
val identity_id : t -> Proto_id.t
110
+
val email_id : t -> Proto_id.t
111
+
val thread_id : t -> Proto_id.t
110
112
val envelope : t -> Envelope.t option
111
113
val send_at : t -> Ptime.t
112
114
val undo_status : t -> undo_status
113
115
val delivery_status : t -> (string * Delivery_status.t) list option
114
-
val dsn_blob_ids : t -> Jmap_proto.Id.t list
115
-
val mdn_blob_ids : t -> Jmap_proto.Id.t list
116
+
val dsn_blob_ids : t -> Proto_id.t list
117
+
val mdn_blob_ids : t -> Proto_id.t list
116
118
117
119
val jsont : t Jsont.t
118
120
···
120
122
121
123
module Filter_condition : sig
122
124
type t = {
123
-
identity_ids : Jmap_proto.Id.t list option;
124
-
email_ids : Jmap_proto.Id.t list option;
125
-
thread_ids : Jmap_proto.Id.t list option;
125
+
identity_ids : Proto_id.t list option;
126
+
email_ids : Proto_id.t list option;
127
+
thread_ids : Proto_id.t list option;
126
128
undo_status : undo_status option;
127
129
before : Ptime.t option;
128
130
after : Ptime.t option;
+4
-4
proto/mail/thread.ml
lib/mail/mail_thread.ml
+4
-4
proto/mail/thread.ml
lib/mail/mail_thread.ml
···
4
4
---------------------------------------------------------------------------*)
5
5
6
6
type t = {
7
-
id : Jmap_proto.Id.t;
8
-
email_ids : Jmap_proto.Id.t list;
7
+
id : Proto_id.t;
8
+
email_ids : Proto_id.t list;
9
9
}
10
10
11
11
let id t = t.id
···
16
16
let jsont =
17
17
let kind = "Thread" in
18
18
Jsont.Object.map ~kind make
19
-
|> Jsont.Object.mem "id" Jmap_proto.Id.jsont ~enc:id
20
-
|> Jsont.Object.mem "emailIds" (Jsont.list Jmap_proto.Id.jsont) ~enc:email_ids
19
+
|> Jsont.Object.mem "id" Proto_id.jsont ~enc:id
20
+
|> Jsont.Object.mem "emailIds" (Jsont.list Proto_id.jsont) ~enc:email_ids
21
21
|> Jsont.Object.finish
+7
-5
proto/mail/thread.mli
lib/mail/mail_thread.mli
+7
-5
proto/mail/thread.mli
lib/mail/mail_thread.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** Thread type as defined in RFC 8621 Section 3 *)
6
+
(** Thread type as defined in RFC 8621 Section 3
7
+
8
+
@canonical Jmap.Proto.Thread *)
7
9
8
10
type t = {
9
-
id : Jmap_proto.Id.t;
11
+
id : Proto_id.t;
10
12
(** Server-assigned thread id. *)
11
-
email_ids : Jmap_proto.Id.t list;
13
+
email_ids : Proto_id.t list;
12
14
(** Ids of emails in this thread, in date order. *)
13
15
}
14
16
15
-
val id : t -> Jmap_proto.Id.t
16
-
val email_ids : t -> Jmap_proto.Id.t list
17
+
val id : t -> Proto_id.t
18
+
val email_ids : t -> Proto_id.t list
17
19
18
20
val jsont : t Jsont.t
+5
-5
proto/mail/vacation.ml
lib/mail/mail_vacation.ml
+5
-5
proto/mail/vacation.ml
lib/mail/mail_vacation.ml
···
4
4
---------------------------------------------------------------------------*)
5
5
6
6
type t = {
7
-
id : Jmap_proto.Id.t;
7
+
id : Proto_id.t;
8
8
is_enabled : bool;
9
9
from_date : Ptime.t option;
10
10
to_date : Ptime.t option;
···
21
21
let text_body t = t.text_body
22
22
let html_body t = t.html_body
23
23
24
-
let singleton_id = Jmap_proto.Id.of_string_exn "singleton"
24
+
let singleton_id = Proto_id.of_string_exn "singleton"
25
25
26
26
let make id is_enabled from_date to_date subject text_body html_body =
27
27
{ id; is_enabled; from_date; to_date; subject; text_body; html_body }
···
31
31
(* subject, textBody, htmlBody can be null per RFC 8621 Section 8 *)
32
32
let nullable_string = Jsont.(option string) in
33
33
Jsont.Object.map ~kind make
34
-
|> Jsont.Object.mem "id" Jmap_proto.Id.jsont ~enc:id
34
+
|> Jsont.Object.mem "id" Proto_id.jsont ~enc:id
35
35
|> Jsont.Object.mem "isEnabled" Jsont.bool ~enc:is_enabled
36
-
|> Jsont.Object.opt_mem "fromDate" Jmap_proto.Date.Utc.jsont ~enc:from_date
37
-
|> Jsont.Object.opt_mem "toDate" Jmap_proto.Date.Utc.jsont ~enc:to_date
36
+
|> Jsont.Object.opt_mem "fromDate" Proto_date.Utc.jsont ~enc:from_date
37
+
|> Jsont.Object.opt_mem "toDate" Proto_date.Utc.jsont ~enc:to_date
38
38
|> Jsont.Object.mem "subject" nullable_string
39
39
~dec_absent:None ~enc_omit:Option.is_none ~enc:subject
40
40
|> Jsont.Object.mem "textBody" nullable_string
+6
-4
proto/mail/vacation.mli
lib/mail/mail_vacation.mli
+6
-4
proto/mail/vacation.mli
lib/mail/mail_vacation.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** VacationResponse type as defined in RFC 8621 Section 8 *)
6
+
(** VacationResponse type as defined in RFC 8621 Section 8
7
+
8
+
@canonical Jmap.Proto.Vacation *)
7
9
8
10
type t = {
9
-
id : Jmap_proto.Id.t;
11
+
id : Proto_id.t;
10
12
(** Always "singleton" - there is only one vacation response. *)
11
13
is_enabled : bool;
12
14
(** Whether the vacation response is active. *)
···
22
24
(** HTML body. *)
23
25
}
24
26
25
-
val id : t -> Jmap_proto.Id.t
27
+
val id : t -> Proto_id.t
26
28
val is_enabled : t -> bool
27
29
val from_date : t -> Ptime.t option
28
30
val to_date : t -> Ptime.t option
···
33
35
val jsont : t Jsont.t
34
36
35
37
(** The singleton id for VacationResponse. *)
36
-
val singleton_id : Jmap_proto.Id.t
38
+
val singleton_id : Proto_id.t
-317
proto/method_.ml
-317
proto/method_.ml
···
1
-
(*---------------------------------------------------------------------------
2
-
Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3
-
SPDX-License-Identifier: ISC
4
-
---------------------------------------------------------------------------*)
5
-
6
-
(* Foo/get *)
7
-
8
-
type get_args = {
9
-
account_id : Id.t;
10
-
ids : Id.t list option;
11
-
properties : string list option;
12
-
}
13
-
14
-
let get_args ~account_id ?ids ?properties () =
15
-
{ account_id; ids; properties }
16
-
17
-
let get_args_make account_id ids properties =
18
-
{ account_id; ids; properties }
19
-
20
-
let get_args_jsont =
21
-
let kind = "GetArgs" in
22
-
Jsont.Object.map ~kind get_args_make
23
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun a -> a.account_id)
24
-
|> Jsont.Object.opt_mem "ids" (Jsont.list Id.jsont) ~enc:(fun a -> a.ids)
25
-
|> Jsont.Object.opt_mem "properties" (Jsont.list Jsont.string) ~enc:(fun a -> a.properties)
26
-
|> Jsont.Object.finish
27
-
28
-
type 'a get_response = {
29
-
account_id : Id.t;
30
-
state : string;
31
-
list : 'a list;
32
-
not_found : Id.t list;
33
-
}
34
-
35
-
let get_response_jsont (type a) (obj_jsont : a Jsont.t) : a get_response Jsont.t =
36
-
let kind = "GetResponse" in
37
-
let make account_id state list not_found =
38
-
{ account_id; state; list; not_found }
39
-
in
40
-
Jsont.Object.map ~kind make
41
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun r -> r.account_id)
42
-
|> Jsont.Object.mem "state" Jsont.string ~enc:(fun r -> r.state)
43
-
|> Jsont.Object.mem "list" (Jsont.list obj_jsont) ~enc:(fun r -> r.list)
44
-
|> Jsont.Object.mem "notFound" (Jsont.list Id.jsont) ~enc:(fun r -> r.not_found)
45
-
|> Jsont.Object.finish
46
-
47
-
(* Foo/changes *)
48
-
49
-
type changes_args = {
50
-
account_id : Id.t;
51
-
since_state : string;
52
-
max_changes : int64 option;
53
-
}
54
-
55
-
let changes_args ~account_id ~since_state ?max_changes () =
56
-
{ account_id; since_state; max_changes }
57
-
58
-
let changes_args_make account_id since_state max_changes =
59
-
{ account_id; since_state; max_changes }
60
-
61
-
let changes_args_jsont =
62
-
let kind = "ChangesArgs" in
63
-
Jsont.Object.map ~kind changes_args_make
64
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun a -> a.account_id)
65
-
|> Jsont.Object.mem "sinceState" Jsont.string ~enc:(fun a -> a.since_state)
66
-
|> Jsont.Object.opt_mem "maxChanges" Int53.Unsigned.jsont ~enc:(fun a -> a.max_changes)
67
-
|> Jsont.Object.finish
68
-
69
-
type changes_response = {
70
-
account_id : Id.t;
71
-
old_state : string;
72
-
new_state : string;
73
-
has_more_changes : bool;
74
-
created : Id.t list;
75
-
updated : Id.t list;
76
-
destroyed : Id.t list;
77
-
}
78
-
79
-
let changes_response_make account_id old_state new_state has_more_changes
80
-
created updated destroyed =
81
-
{ account_id; old_state; new_state; has_more_changes; created; updated; destroyed }
82
-
83
-
let changes_response_jsont =
84
-
let kind = "ChangesResponse" in
85
-
Jsont.Object.map ~kind changes_response_make
86
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun r -> r.account_id)
87
-
|> Jsont.Object.mem "oldState" Jsont.string ~enc:(fun r -> r.old_state)
88
-
|> Jsont.Object.mem "newState" Jsont.string ~enc:(fun r -> r.new_state)
89
-
|> Jsont.Object.mem "hasMoreChanges" Jsont.bool ~enc:(fun r -> r.has_more_changes)
90
-
|> Jsont.Object.mem "created" (Jsont.list Id.jsont) ~enc:(fun r -> r.created)
91
-
|> Jsont.Object.mem "updated" (Jsont.list Id.jsont) ~enc:(fun r -> r.updated)
92
-
|> Jsont.Object.mem "destroyed" (Jsont.list Id.jsont) ~enc:(fun r -> r.destroyed)
93
-
|> Jsont.Object.finish
94
-
95
-
(* Foo/set *)
96
-
97
-
type 'a set_args = {
98
-
account_id : Id.t;
99
-
if_in_state : string option;
100
-
create : (Id.t * 'a) list option;
101
-
update : (Id.t * Jsont.json) list option;
102
-
destroy : Id.t list option;
103
-
}
104
-
105
-
let set_args ~account_id ?if_in_state ?create ?update ?destroy () =
106
-
{ account_id; if_in_state; create; update; destroy }
107
-
108
-
let set_args_jsont (type a) (obj_jsont : a Jsont.t) : a set_args Jsont.t =
109
-
let kind = "SetArgs" in
110
-
let make account_id if_in_state create update destroy =
111
-
{ account_id; if_in_state; create; update; destroy }
112
-
in
113
-
Jsont.Object.map ~kind make
114
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun a -> a.account_id)
115
-
|> Jsont.Object.opt_mem "ifInState" Jsont.string ~enc:(fun a -> a.if_in_state)
116
-
|> Jsont.Object.opt_mem "create" (Json_map.of_id obj_jsont) ~enc:(fun a -> a.create)
117
-
|> Jsont.Object.opt_mem "update" (Json_map.of_id Jsont.json) ~enc:(fun a -> a.update)
118
-
|> Jsont.Object.opt_mem "destroy" (Jsont.list Id.jsont) ~enc:(fun a -> a.destroy)
119
-
|> Jsont.Object.finish
120
-
121
-
type 'a set_response = {
122
-
account_id : Id.t;
123
-
old_state : string option;
124
-
new_state : string;
125
-
created : (Id.t * 'a) list option;
126
-
updated : (Id.t * 'a option) list option;
127
-
destroyed : Id.t list option;
128
-
not_created : (Id.t * Error.set_error) list option;
129
-
not_updated : (Id.t * Error.set_error) list option;
130
-
not_destroyed : (Id.t * Error.set_error) list option;
131
-
}
132
-
133
-
let set_response_jsont (type a) (obj_jsont : a Jsont.t) : a set_response Jsont.t =
134
-
let kind = "SetResponse" in
135
-
let make account_id old_state new_state created updated destroyed
136
-
not_created not_updated not_destroyed =
137
-
{ account_id; old_state; new_state; created; updated; destroyed;
138
-
not_created; not_updated; not_destroyed }
139
-
in
140
-
(* For updated values, the server may return null or an object - RFC 8620 Section 5.3 *)
141
-
(* "Id[Foo|null]" means map values can be null, use Jsont.option to handle this *)
142
-
let nullable_obj = Jsont.(option obj_jsont) in
143
-
Jsont.Object.map ~kind make
144
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun r -> r.account_id)
145
-
|> Jsont.Object.opt_mem "oldState" Jsont.string ~enc:(fun r -> r.old_state)
146
-
|> Jsont.Object.mem "newState" Jsont.string ~enc:(fun r -> r.new_state)
147
-
|> Jsont.Object.opt_mem "created" (Json_map.of_id obj_jsont) ~enc:(fun r -> r.created)
148
-
|> Jsont.Object.opt_mem "updated" (Json_map.of_id nullable_obj) ~enc:(fun r -> r.updated)
149
-
|> Jsont.Object.opt_mem "destroyed" (Jsont.list Id.jsont) ~enc:(fun r -> r.destroyed)
150
-
|> Jsont.Object.opt_mem "notCreated" (Json_map.of_id Error.set_error_jsont) ~enc:(fun r -> r.not_created)
151
-
|> Jsont.Object.opt_mem "notUpdated" (Json_map.of_id Error.set_error_jsont) ~enc:(fun r -> r.not_updated)
152
-
|> Jsont.Object.opt_mem "notDestroyed" (Json_map.of_id Error.set_error_jsont) ~enc:(fun r -> r.not_destroyed)
153
-
|> Jsont.Object.finish
154
-
155
-
(* Foo/copy *)
156
-
157
-
type 'a copy_args = {
158
-
from_account_id : Id.t;
159
-
if_from_in_state : string option;
160
-
account_id : Id.t;
161
-
if_in_state : string option;
162
-
create : (Id.t * 'a) list;
163
-
on_success_destroy_original : bool;
164
-
destroy_from_if_in_state : string option;
165
-
}
166
-
167
-
let copy_args_jsont (type a) (obj_jsont : a Jsont.t) : a copy_args Jsont.t =
168
-
let kind = "CopyArgs" in
169
-
let make from_account_id if_from_in_state account_id if_in_state create
170
-
on_success_destroy_original destroy_from_if_in_state =
171
-
{ from_account_id; if_from_in_state; account_id; if_in_state; create;
172
-
on_success_destroy_original; destroy_from_if_in_state }
173
-
in
174
-
Jsont.Object.map ~kind make
175
-
|> Jsont.Object.mem "fromAccountId" Id.jsont ~enc:(fun a -> a.from_account_id)
176
-
|> Jsont.Object.opt_mem "ifFromInState" Jsont.string ~enc:(fun a -> a.if_from_in_state)
177
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun a -> a.account_id)
178
-
|> Jsont.Object.opt_mem "ifInState" Jsont.string ~enc:(fun a -> a.if_in_state)
179
-
|> Jsont.Object.mem "create" (Json_map.of_id obj_jsont) ~enc:(fun a -> a.create)
180
-
|> Jsont.Object.mem "onSuccessDestroyOriginal" Jsont.bool ~dec_absent:false
181
-
~enc:(fun a -> a.on_success_destroy_original)
182
-
~enc_omit:(fun b -> not b)
183
-
|> Jsont.Object.opt_mem "destroyFromIfInState" Jsont.string ~enc:(fun a -> a.destroy_from_if_in_state)
184
-
|> Jsont.Object.finish
185
-
186
-
type 'a copy_response = {
187
-
from_account_id : Id.t;
188
-
account_id : Id.t;
189
-
old_state : string option;
190
-
new_state : string;
191
-
created : (Id.t * 'a) list option;
192
-
not_created : (Id.t * Error.set_error) list option;
193
-
}
194
-
195
-
let copy_response_jsont (type a) (obj_jsont : a Jsont.t) : a copy_response Jsont.t =
196
-
let kind = "CopyResponse" in
197
-
let make from_account_id account_id old_state new_state created not_created =
198
-
{ from_account_id; account_id; old_state; new_state; created; not_created }
199
-
in
200
-
Jsont.Object.map ~kind make
201
-
|> Jsont.Object.mem "fromAccountId" Id.jsont ~enc:(fun r -> r.from_account_id)
202
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun r -> r.account_id)
203
-
|> Jsont.Object.opt_mem "oldState" Jsont.string ~enc:(fun r -> r.old_state)
204
-
|> Jsont.Object.mem "newState" Jsont.string ~enc:(fun r -> r.new_state)
205
-
|> Jsont.Object.opt_mem "created" (Json_map.of_id obj_jsont) ~enc:(fun r -> r.created)
206
-
|> Jsont.Object.opt_mem "notCreated" (Json_map.of_id Error.set_error_jsont) ~enc:(fun r -> r.not_created)
207
-
|> Jsont.Object.finish
208
-
209
-
(* Foo/query *)
210
-
211
-
type 'filter query_args = {
212
-
account_id : Id.t;
213
-
filter : 'filter Filter.filter option;
214
-
sort : Filter.comparator list option;
215
-
position : int64;
216
-
anchor : Id.t option;
217
-
anchor_offset : int64;
218
-
limit : int64 option;
219
-
calculate_total : bool;
220
-
}
221
-
222
-
let query_args ~account_id ?filter ?sort ?(position = 0L) ?anchor
223
-
?(anchor_offset = 0L) ?limit ?(calculate_total = false) () =
224
-
{ account_id; filter; sort; position; anchor; anchor_offset; limit; calculate_total }
225
-
226
-
let query_args_jsont (type f) (filter_cond_jsont : f Jsont.t) : f query_args Jsont.t =
227
-
let kind = "QueryArgs" in
228
-
let make account_id filter sort position anchor anchor_offset limit calculate_total =
229
-
{ account_id; filter; sort; position; anchor; anchor_offset; limit; calculate_total }
230
-
in
231
-
Jsont.Object.map ~kind make
232
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun a -> a.account_id)
233
-
|> Jsont.Object.opt_mem "filter" (Filter.filter_jsont filter_cond_jsont) ~enc:(fun a -> a.filter)
234
-
|> Jsont.Object.opt_mem "sort" (Jsont.list Filter.comparator_jsont) ~enc:(fun a -> a.sort)
235
-
|> Jsont.Object.mem "position" Int53.Signed.jsont ~dec_absent:0L ~enc:(fun a -> a.position)
236
-
~enc_omit:(fun p -> p = 0L)
237
-
|> Jsont.Object.opt_mem "anchor" Id.jsont ~enc:(fun a -> a.anchor)
238
-
|> Jsont.Object.mem "anchorOffset" Int53.Signed.jsont ~dec_absent:0L ~enc:(fun a -> a.anchor_offset)
239
-
~enc_omit:(fun o -> o = 0L)
240
-
|> Jsont.Object.opt_mem "limit" Int53.Unsigned.jsont ~enc:(fun a -> a.limit)
241
-
|> Jsont.Object.mem "calculateTotal" Jsont.bool ~dec_absent:false ~enc:(fun a -> a.calculate_total)
242
-
~enc_omit:(fun b -> not b)
243
-
|> Jsont.Object.finish
244
-
245
-
type query_response = {
246
-
account_id : Id.t;
247
-
query_state : string;
248
-
can_calculate_changes : bool;
249
-
position : int64;
250
-
ids : Id.t list;
251
-
total : int64 option;
252
-
}
253
-
254
-
let query_response_make account_id query_state can_calculate_changes position ids total =
255
-
{ account_id; query_state; can_calculate_changes; position; ids; total }
256
-
257
-
let query_response_jsont =
258
-
let kind = "QueryResponse" in
259
-
Jsont.Object.map ~kind query_response_make
260
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun r -> r.account_id)
261
-
|> Jsont.Object.mem "queryState" Jsont.string ~enc:(fun r -> r.query_state)
262
-
|> Jsont.Object.mem "canCalculateChanges" Jsont.bool ~enc:(fun r -> r.can_calculate_changes)
263
-
|> Jsont.Object.mem "position" Int53.Unsigned.jsont ~enc:(fun r -> r.position)
264
-
|> Jsont.Object.mem "ids" (Jsont.list Id.jsont) ~enc:(fun r -> r.ids)
265
-
|> Jsont.Object.opt_mem "total" Int53.Unsigned.jsont ~enc:(fun r -> r.total)
266
-
|> Jsont.Object.finish
267
-
268
-
(* Foo/queryChanges *)
269
-
270
-
type 'filter query_changes_args = {
271
-
account_id : Id.t;
272
-
filter : 'filter Filter.filter option;
273
-
sort : Filter.comparator list option;
274
-
since_query_state : string;
275
-
max_changes : int64 option;
276
-
up_to_id : Id.t option;
277
-
calculate_total : bool;
278
-
}
279
-
280
-
let query_changes_args_jsont (type f) (filter_cond_jsont : f Jsont.t) : f query_changes_args Jsont.t =
281
-
let kind = "QueryChangesArgs" in
282
-
let make account_id filter sort since_query_state max_changes up_to_id calculate_total =
283
-
{ account_id; filter; sort; since_query_state; max_changes; up_to_id; calculate_total }
284
-
in
285
-
Jsont.Object.map ~kind make
286
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun a -> a.account_id)
287
-
|> Jsont.Object.opt_mem "filter" (Filter.filter_jsont filter_cond_jsont) ~enc:(fun a -> a.filter)
288
-
|> Jsont.Object.opt_mem "sort" (Jsont.list Filter.comparator_jsont) ~enc:(fun a -> a.sort)
289
-
|> Jsont.Object.mem "sinceQueryState" Jsont.string ~enc:(fun a -> a.since_query_state)
290
-
|> Jsont.Object.opt_mem "maxChanges" Int53.Unsigned.jsont ~enc:(fun a -> a.max_changes)
291
-
|> Jsont.Object.opt_mem "upToId" Id.jsont ~enc:(fun a -> a.up_to_id)
292
-
|> Jsont.Object.mem "calculateTotal" Jsont.bool ~dec_absent:false ~enc:(fun a -> a.calculate_total)
293
-
~enc_omit:(fun b -> not b)
294
-
|> Jsont.Object.finish
295
-
296
-
type query_changes_response = {
297
-
account_id : Id.t;
298
-
old_query_state : string;
299
-
new_query_state : string;
300
-
total : int64 option;
301
-
removed : Id.t list;
302
-
added : Filter.added_item list;
303
-
}
304
-
305
-
let query_changes_response_make account_id old_query_state new_query_state total removed added =
306
-
{ account_id; old_query_state; new_query_state; total; removed; added }
307
-
308
-
let query_changes_response_jsont =
309
-
let kind = "QueryChangesResponse" in
310
-
Jsont.Object.map ~kind query_changes_response_make
311
-
|> Jsont.Object.mem "accountId" Id.jsont ~enc:(fun r -> r.account_id)
312
-
|> Jsont.Object.mem "oldQueryState" Jsont.string ~enc:(fun r -> r.old_query_state)
313
-
|> Jsont.Object.mem "newQueryState" Jsont.string ~enc:(fun r -> r.new_query_state)
314
-
|> Jsont.Object.opt_mem "total" Int53.Unsigned.jsont ~enc:(fun r -> r.total)
315
-
|> Jsont.Object.mem "removed" (Jsont.list Id.jsont) ~enc:(fun r -> r.removed)
316
-
|> Jsont.Object.mem "added" (Jsont.list Filter.added_item_jsont) ~enc:(fun r -> r.added)
317
-
|> Jsont.Object.finish
+54
-52
proto/method_.mli
lib/proto/proto_method.mli
+54
-52
proto/method_.mli
lib/proto/proto_method.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** JMAP standard method types as defined in RFC 8620 Section 5 *)
6
+
(** JMAP standard method types as defined in RFC 8620 Section 5
7
+
8
+
@canonical Jmap.Proto.Method *)
7
9
8
10
(** {1 Foo/get} *)
9
11
10
12
(** Arguments for /get methods. *)
11
13
type get_args = {
12
-
account_id : Id.t;
14
+
account_id : Proto_id.t;
13
15
(** The account to fetch from. *)
14
-
ids : Id.t list option;
16
+
ids : Proto_id.t list option;
15
17
(** The ids to fetch. [None] means fetch all. *)
16
18
properties : string list option;
17
19
(** Properties to include. [None] means all. *)
18
20
}
19
21
20
22
val get_args :
21
-
account_id:Id.t ->
22
-
?ids:Id.t list ->
23
+
account_id:Proto_id.t ->
24
+
?ids:Proto_id.t list ->
23
25
?properties:string list ->
24
26
unit ->
25
27
get_args
···
28
30
29
31
(** Response for /get methods. *)
30
32
type 'a get_response = {
31
-
account_id : Id.t;
33
+
account_id : Proto_id.t;
32
34
(** The account fetched from. *)
33
35
state : string;
34
36
(** Current state string. *)
35
37
list : 'a list;
36
38
(** The objects fetched. *)
37
-
not_found : Id.t list;
39
+
not_found : Proto_id.t list;
38
40
(** Ids that were not found. *)
39
41
}
40
42
···
44
46
45
47
(** Arguments for /changes methods. *)
46
48
type changes_args = {
47
-
account_id : Id.t;
49
+
account_id : Proto_id.t;
48
50
since_state : string;
49
51
max_changes : int64 option;
50
52
}
51
53
52
54
val changes_args :
53
-
account_id:Id.t ->
55
+
account_id:Proto_id.t ->
54
56
since_state:string ->
55
57
?max_changes:int64 ->
56
58
unit ->
···
60
62
61
63
(** Response for /changes methods. *)
62
64
type changes_response = {
63
-
account_id : Id.t;
65
+
account_id : Proto_id.t;
64
66
old_state : string;
65
67
new_state : string;
66
68
has_more_changes : bool;
67
-
created : Id.t list;
68
-
updated : Id.t list;
69
-
destroyed : Id.t list;
69
+
created : Proto_id.t list;
70
+
updated : Proto_id.t list;
71
+
destroyed : Proto_id.t list;
70
72
}
71
73
72
74
val changes_response_jsont : changes_response Jsont.t
···
77
79
78
80
The ['a] type parameter is the object type being created/updated. *)
79
81
type 'a set_args = {
80
-
account_id : Id.t;
82
+
account_id : Proto_id.t;
81
83
if_in_state : string option;
82
84
(** If set, only apply if current state matches. *)
83
-
create : (Id.t * 'a) list option;
85
+
create : (Proto_id.t * 'a) list option;
84
86
(** Objects to create, keyed by temporary id. *)
85
-
update : (Id.t * Jsont.json) list option;
87
+
update : (Proto_id.t * Jsont.json) list option;
86
88
(** Objects to update. Value is a PatchObject. *)
87
-
destroy : Id.t list option;
89
+
destroy : Proto_id.t list option;
88
90
(** Ids to destroy. *)
89
91
}
90
92
91
93
val set_args :
92
-
account_id:Id.t ->
94
+
account_id:Proto_id.t ->
93
95
?if_in_state:string ->
94
-
?create:(Id.t * 'a) list ->
95
-
?update:(Id.t * Jsont.json) list ->
96
-
?destroy:Id.t list ->
96
+
?create:(Proto_id.t * 'a) list ->
97
+
?update:(Proto_id.t * Jsont.json) list ->
98
+
?destroy:Proto_id.t list ->
97
99
unit ->
98
100
'a set_args
99
101
···
101
103
102
104
(** Response for /set methods. *)
103
105
type 'a set_response = {
104
-
account_id : Id.t;
106
+
account_id : Proto_id.t;
105
107
old_state : string option;
106
108
new_state : string;
107
-
created : (Id.t * 'a) list option;
109
+
created : (Proto_id.t * 'a) list option;
108
110
(** Successfully created objects, keyed by temporary id. *)
109
-
updated : (Id.t * 'a option) list option;
111
+
updated : (Proto_id.t * 'a option) list option;
110
112
(** Successfully updated objects. Value may include server-set properties. *)
111
-
destroyed : Id.t list option;
113
+
destroyed : Proto_id.t list option;
112
114
(** Successfully destroyed ids. *)
113
-
not_created : (Id.t * Error.set_error) list option;
115
+
not_created : (Proto_id.t * Proto_error.set_error) list option;
114
116
(** Failed creates. *)
115
-
not_updated : (Id.t * Error.set_error) list option;
117
+
not_updated : (Proto_id.t * Proto_error.set_error) list option;
116
118
(** Failed updates. *)
117
-
not_destroyed : (Id.t * Error.set_error) list option;
119
+
not_destroyed : (Proto_id.t * Proto_error.set_error) list option;
118
120
(** Failed destroys. *)
119
121
}
120
122
···
124
126
125
127
(** Arguments for /copy methods. *)
126
128
type 'a copy_args = {
127
-
from_account_id : Id.t;
129
+
from_account_id : Proto_id.t;
128
130
if_from_in_state : string option;
129
-
account_id : Id.t;
131
+
account_id : Proto_id.t;
130
132
if_in_state : string option;
131
-
create : (Id.t * 'a) list;
133
+
create : (Proto_id.t * 'a) list;
132
134
on_success_destroy_original : bool;
133
135
destroy_from_if_in_state : string option;
134
136
}
···
137
139
138
140
(** Response for /copy methods. *)
139
141
type 'a copy_response = {
140
-
from_account_id : Id.t;
141
-
account_id : Id.t;
142
+
from_account_id : Proto_id.t;
143
+
account_id : Proto_id.t;
142
144
old_state : string option;
143
145
new_state : string;
144
-
created : (Id.t * 'a) list option;
145
-
not_created : (Id.t * Error.set_error) list option;
146
+
created : (Proto_id.t * 'a) list option;
147
+
not_created : (Proto_id.t * Proto_error.set_error) list option;
146
148
}
147
149
148
150
val copy_response_jsont : 'a Jsont.t -> 'a copy_response Jsont.t
···
151
153
152
154
(** Arguments for /query methods. *)
153
155
type 'filter query_args = {
154
-
account_id : Id.t;
155
-
filter : 'filter Filter.filter option;
156
-
sort : Filter.comparator list option;
156
+
account_id : Proto_id.t;
157
+
filter : 'filter Proto_filter.filter option;
158
+
sort : Proto_filter.comparator list option;
157
159
position : int64;
158
-
anchor : Id.t option;
160
+
anchor : Proto_id.t option;
159
161
anchor_offset : int64;
160
162
limit : int64 option;
161
163
calculate_total : bool;
162
164
}
163
165
164
166
val query_args :
165
-
account_id:Id.t ->
166
-
?filter:'filter Filter.filter ->
167
-
?sort:Filter.comparator list ->
167
+
account_id:Proto_id.t ->
168
+
?filter:'filter Proto_filter.filter ->
169
+
?sort:Proto_filter.comparator list ->
168
170
?position:int64 ->
169
-
?anchor:Id.t ->
171
+
?anchor:Proto_id.t ->
170
172
?anchor_offset:int64 ->
171
173
?limit:int64 ->
172
174
?calculate_total:bool ->
···
177
179
178
180
(** Response for /query methods. *)
179
181
type query_response = {
180
-
account_id : Id.t;
182
+
account_id : Proto_id.t;
181
183
query_state : string;
182
184
can_calculate_changes : bool;
183
185
position : int64;
184
-
ids : Id.t list;
186
+
ids : Proto_id.t list;
185
187
total : int64 option;
186
188
}
187
189
···
191
193
192
194
(** Arguments for /queryChanges methods. *)
193
195
type 'filter query_changes_args = {
194
-
account_id : Id.t;
195
-
filter : 'filter Filter.filter option;
196
-
sort : Filter.comparator list option;
196
+
account_id : Proto_id.t;
197
+
filter : 'filter Proto_filter.filter option;
198
+
sort : Proto_filter.comparator list option;
197
199
since_query_state : string;
198
200
max_changes : int64 option;
199
-
up_to_id : Id.t option;
201
+
up_to_id : Proto_id.t option;
200
202
calculate_total : bool;
201
203
}
202
204
···
204
206
205
207
(** Response for /queryChanges methods. *)
206
208
type query_changes_response = {
207
-
account_id : Id.t;
209
+
account_id : Proto_id.t;
208
210
old_query_state : string;
209
211
new_query_state : string;
210
212
total : int64 option;
211
-
removed : Id.t list;
212
-
added : Filter.added_item list;
213
+
removed : Proto_id.t list;
214
+
added : Proto_filter.added_item list;
213
215
}
214
216
215
217
val query_changes_response_jsont : query_changes_response Jsont.t
+16
-16
proto/push.ml
lib/proto/proto_push.ml
+16
-16
proto/push.ml
lib/proto/proto_push.ml
···
11
11
12
12
type t = {
13
13
type_ : string;
14
-
changed : (Id.t * type_state list) list;
14
+
changed : (Proto_id.t * type_state list) list;
15
15
}
16
16
17
17
(* The changed object is account_id -> { typeName: state } *)
18
18
let changed_jsont =
19
19
let kind = "Changed" in
20
20
(* Inner is type -> state string map *)
21
-
let type_states_jsont = Json_map.of_string Jsont.string in
21
+
let type_states_jsont = Proto_json_map.of_string Jsont.string in
22
22
(* Convert list of (string * string) to type_state list *)
23
23
let decode_type_states pairs =
24
24
List.map (fun (type_name, state) -> { type_name; state }) pairs
···
26
26
let encode_type_states states =
27
27
List.map (fun ts -> (ts.type_name, ts.state)) states
28
28
in
29
-
Json_map.of_id
29
+
Proto_json_map.of_id
30
30
(Jsont.map ~kind ~dec:decode_type_states ~enc:encode_type_states type_states_jsont)
31
31
32
32
let make type_ changed = { type_; changed }
···
54
54
|> Jsont.Object.finish
55
55
56
56
type t = {
57
-
id : Id.t;
57
+
id : Proto_id.t;
58
58
device_client_id : string;
59
59
url : string;
60
60
keys : push_keys option;
···
77
77
let jsont =
78
78
let kind = "PushSubscription" in
79
79
Jsont.Object.map ~kind make
80
-
|> Jsont.Object.mem "id" Id.jsont ~enc:id
80
+
|> Jsont.Object.mem "id" Proto_id.jsont ~enc:id
81
81
|> Jsont.Object.mem "deviceClientId" Jsont.string ~enc:device_client_id
82
82
|> Jsont.Object.mem "url" Jsont.string ~enc:url
83
83
|> Jsont.Object.opt_mem "keys" push_keys_jsont ~enc:keys
84
84
|> Jsont.Object.opt_mem "verificationCode" Jsont.string ~enc:verification_code
85
-
|> Jsont.Object.opt_mem "expires" Date.Utc.jsont ~enc:expires
85
+
|> Jsont.Object.opt_mem "expires" Proto_date.Utc.jsont ~enc:expires
86
86
|> Jsont.Object.opt_mem "types" (Jsont.list Jsont.string) ~enc:types
87
87
|> Jsont.Object.finish
88
88
89
-
let get_args_jsont = Method_.get_args_jsont
90
-
let get_response_jsont = Method_.get_response_jsont jsont
89
+
let get_args_jsont = Proto_method.get_args_jsont
90
+
let get_response_jsont = Proto_method.get_response_jsont jsont
91
91
92
92
type create_args = {
93
93
device_client_id : string;
···
111
111
|> Jsont.Object.finish
112
112
113
113
type set_args = {
114
-
account_id : Id.t option;
114
+
account_id : Proto_id.t option;
115
115
if_in_state : string option;
116
-
create : (Id.t * create_args) list option;
117
-
update : (Id.t * Jsont.json) list option;
118
-
destroy : Id.t list option;
116
+
create : (Proto_id.t * create_args) list option;
117
+
update : (Proto_id.t * Jsont.json) list option;
118
+
destroy : Proto_id.t list option;
119
119
}
120
120
121
121
let set_args_make account_id if_in_state create update destroy =
···
124
124
let set_args_jsont =
125
125
let kind = "PushSubscription/set args" in
126
126
Jsont.Object.map ~kind set_args_make
127
-
|> Jsont.Object.opt_mem "accountId" Id.jsont ~enc:(fun a -> a.account_id)
127
+
|> Jsont.Object.opt_mem "accountId" Proto_id.jsont ~enc:(fun a -> a.account_id)
128
128
|> Jsont.Object.opt_mem "ifInState" Jsont.string ~enc:(fun a -> a.if_in_state)
129
-
|> Jsont.Object.opt_mem "create" (Json_map.of_id create_args_jsont) ~enc:(fun a -> a.create)
130
-
|> Jsont.Object.opt_mem "update" (Json_map.of_id Jsont.json) ~enc:(fun a -> a.update)
131
-
|> Jsont.Object.opt_mem "destroy" (Jsont.list Id.jsont) ~enc:(fun a -> a.destroy)
129
+
|> Jsont.Object.opt_mem "create" (Proto_json_map.of_id create_args_jsont) ~enc:(fun a -> a.create)
130
+
|> Jsont.Object.opt_mem "update" (Proto_json_map.of_id Jsont.json) ~enc:(fun a -> a.update)
131
+
|> Jsont.Object.opt_mem "destroy" (Jsont.list Proto_id.jsont) ~enc:(fun a -> a.destroy)
132
132
|> Jsont.Object.finish
+12
-10
proto/push.mli
lib/proto/proto_push.mli
+12
-10
proto/push.mli
lib/proto/proto_push.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** JMAP push types as defined in RFC 8620 Section 7 *)
6
+
(** JMAP push types as defined in RFC 8620 Section 7
7
+
8
+
@canonical Jmap.Proto.Push *)
7
9
8
10
(** {1 StateChange} *)
9
11
···
19
21
type t = {
20
22
type_ : string;
21
23
(** Always "StateChange". *)
22
-
changed : (Id.t * type_state list) list;
24
+
changed : (Proto_id.t * type_state list) list;
23
25
(** Map of account id to list of type state changes. *)
24
26
}
25
27
···
40
42
41
43
(** A push subscription object. *)
42
44
type t = {
43
-
id : Id.t;
45
+
id : Proto_id.t;
44
46
(** Server-assigned subscription id. *)
45
47
device_client_id : string;
46
48
(** Client-provided device identifier. *)
···
56
58
(** Data types to receive notifications for. [None] means all. *)
57
59
}
58
60
59
-
val id : t -> Id.t
61
+
val id : t -> Proto_id.t
60
62
val device_client_id : t -> string
61
63
val url : t -> string
62
64
val keys : t -> push_keys option
···
70
72
(** {1 PushSubscription Methods} *)
71
73
72
74
(** Arguments for PushSubscription/get. *)
73
-
val get_args_jsont : Method_.get_args Jsont.t
75
+
val get_args_jsont : Proto_method.get_args Jsont.t
74
76
75
77
(** Response for PushSubscription/get. *)
76
-
val get_response_jsont : t Method_.get_response Jsont.t
78
+
val get_response_jsont : t Proto_method.get_response Jsont.t
77
79
78
80
(** Arguments for PushSubscription/set. *)
79
81
type set_args = {
80
-
account_id : Id.t option;
82
+
account_id : Proto_id.t option;
81
83
(** Not used for PushSubscription. *)
82
84
if_in_state : string option;
83
-
create : (Id.t * create_args) list option;
84
-
update : (Id.t * Jsont.json) list option;
85
-
destroy : Id.t list option;
85
+
create : (Proto_id.t * create_args) list option;
86
+
update : (Proto_id.t * Jsont.json) list option;
87
+
destroy : Proto_id.t list option;
86
88
}
87
89
88
90
and create_args = {
+4
-4
proto/request.ml
lib/proto/proto_request.ml
+4
-4
proto/request.ml
lib/proto/proto_request.ml
···
5
5
6
6
type t = {
7
7
using : string list;
8
-
method_calls : Invocation.t list;
9
-
created_ids : (Id.t * Id.t) list option;
8
+
method_calls : Proto_invocation.t list;
9
+
created_ids : (Proto_id.t * Proto_id.t) list option;
10
10
}
11
11
12
12
let create ~using ~method_calls ?created_ids () =
···
23
23
let kind = "Request" in
24
24
Jsont.Object.map ~kind make
25
25
|> Jsont.Object.mem "using" (Jsont.list Jsont.string) ~enc:using
26
-
|> Jsont.Object.mem "methodCalls" (Jsont.list Invocation.jsont) ~enc:method_calls
27
-
|> Jsont.Object.opt_mem "createdIds" (Json_map.of_id Id.jsont) ~enc:created_ids
26
+
|> Jsont.Object.mem "methodCalls" (Jsont.list Proto_invocation.jsont) ~enc:method_calls
27
+
|> Jsont.Object.opt_mem "createdIds" (Proto_json_map.of_id Proto_id.jsont) ~enc:created_ids
28
28
|> Jsont.Object.finish
29
29
30
30
let single ~using invocation =
+11
-9
proto/request.mli
lib/proto/proto_request.mli
+11
-9
proto/request.mli
lib/proto/proto_request.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** JMAP request object as defined in RFC 8620 Section 3.3 *)
6
+
(** JMAP request object as defined in RFC 8620 Section 3.3
7
+
8
+
@canonical Jmap.Proto.Request *)
7
9
8
10
type t = {
9
11
using : string list;
10
12
(** Capability URIs required for this request. *)
11
-
method_calls : Invocation.t list;
13
+
method_calls : Proto_invocation.t list;
12
14
(** The method calls to execute. *)
13
-
created_ids : (Id.t * Id.t) list option;
15
+
created_ids : (Proto_id.t * Proto_id.t) list option;
14
16
(** Map of client-created temporary ids to server-assigned ids.
15
17
Used for result references in batch operations. *)
16
18
}
17
19
18
20
val create :
19
21
using:string list ->
20
-
method_calls:Invocation.t list ->
21
-
?created_ids:(Id.t * Id.t) list ->
22
+
method_calls:Proto_invocation.t list ->
23
+
?created_ids:(Proto_id.t * Proto_id.t) list ->
22
24
unit ->
23
25
t
24
26
(** [create ~using ~method_calls ?created_ids ()] creates a JMAP request. *)
25
27
26
28
val using : t -> string list
27
-
val method_calls : t -> Invocation.t list
28
-
val created_ids : t -> (Id.t * Id.t) list option
29
+
val method_calls : t -> Proto_invocation.t list
30
+
val created_ids : t -> (Proto_id.t * Proto_id.t) list option
29
31
30
32
val jsont : t Jsont.t
31
33
(** JSON codec for JMAP requests. *)
···
34
36
35
37
val single :
36
38
using:string list ->
37
-
Invocation.t ->
39
+
Proto_invocation.t ->
38
40
t
39
41
(** [single ~using invocation] creates a request with a single method call. *)
40
42
41
43
val batch :
42
44
using:string list ->
43
-
Invocation.t list ->
45
+
Proto_invocation.t list ->
44
46
t
45
47
(** [batch ~using invocations] creates a request with multiple method calls. *)
+7
-7
proto/response.ml
lib/proto/proto_response.ml
+7
-7
proto/response.ml
lib/proto/proto_response.ml
···
4
4
---------------------------------------------------------------------------*)
5
5
6
6
type t = {
7
-
method_responses : Invocation.t list;
8
-
created_ids : (Id.t * Id.t) list option;
7
+
method_responses : Proto_invocation.t list;
8
+
created_ids : (Proto_id.t * Proto_id.t) list option;
9
9
session_state : string;
10
10
}
11
11
···
19
19
let jsont =
20
20
let kind = "Response" in
21
21
Jsont.Object.map ~kind make
22
-
|> Jsont.Object.mem "methodResponses" (Jsont.list Invocation.jsont) ~enc:method_responses
23
-
|> Jsont.Object.opt_mem "createdIds" (Json_map.of_id Id.jsont) ~enc:created_ids
22
+
|> Jsont.Object.mem "methodResponses" (Jsont.list Proto_invocation.jsont) ~enc:method_responses
23
+
|> Jsont.Object.opt_mem "createdIds" (Proto_json_map.of_id Proto_id.jsont) ~enc:created_ids
24
24
|> Jsont.Object.mem "sessionState" Jsont.string ~enc:session_state
25
25
|> Jsont.Object.finish
26
26
27
27
let find_response method_call_id response =
28
28
List.find_opt
29
-
(fun inv -> Invocation.method_call_id inv = method_call_id)
29
+
(fun inv -> Proto_invocation.method_call_id inv = method_call_id)
30
30
response.method_responses
31
31
32
32
let get_response method_call_id response =
···
35
35
| None -> raise Not_found
36
36
37
37
let is_error invocation =
38
-
String.equal (Invocation.name invocation) "error"
38
+
String.equal (Proto_invocation.name invocation) "error"
39
39
40
40
let get_error invocation =
41
41
if is_error invocation then
42
-
match Jsont.Json.decode' Error.method_error_jsont (Invocation.arguments invocation) with
42
+
match Jsont.Json.decode' Proto_error.method_error_jsont (Proto_invocation.arguments invocation) with
43
43
| Ok v -> Some v
44
44
| Error _ -> None
45
45
else
+11
-9
proto/response.mli
lib/proto/proto_response.mli
+11
-9
proto/response.mli
lib/proto/proto_response.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** JMAP response object as defined in RFC 8620 Section 3.4 *)
6
+
(** JMAP response object as defined in RFC 8620 Section 3.4
7
+
8
+
@canonical Jmap.Proto.Response *)
7
9
8
10
type t = {
9
-
method_responses : Invocation.t list;
11
+
method_responses : Proto_invocation.t list;
10
12
(** The method responses. Each is [methodName, responseArgs, methodCallId]. *)
11
-
created_ids : (Id.t * Id.t) list option;
13
+
created_ids : (Proto_id.t * Proto_id.t) list option;
12
14
(** Map of client-created temporary ids to server-assigned ids. *)
13
15
session_state : string;
14
16
(** Current session state. Changes indicate session data has changed. *)
15
17
}
16
18
17
-
val method_responses : t -> Invocation.t list
18
-
val created_ids : t -> (Id.t * Id.t) list option
19
+
val method_responses : t -> Proto_invocation.t list
20
+
val created_ids : t -> (Proto_id.t * Proto_id.t) list option
19
21
val session_state : t -> string
20
22
21
23
val jsont : t Jsont.t
···
23
25
24
26
(** {1 Response Inspection} *)
25
27
26
-
val find_response : string -> t -> Invocation.t option
28
+
val find_response : string -> t -> Proto_invocation.t option
27
29
(** [find_response method_call_id response] finds the response for a method call. *)
28
30
29
-
val get_response : string -> t -> Invocation.t
31
+
val get_response : string -> t -> Proto_invocation.t
30
32
(** [get_response method_call_id response] gets the response for a method call.
31
33
@raise Not_found if not found. *)
32
34
33
-
val is_error : Invocation.t -> bool
35
+
val is_error : Proto_invocation.t -> bool
34
36
(** [is_error invocation] returns [true] if the invocation is an error response. *)
35
37
36
-
val get_error : Invocation.t -> Error.method_error option
38
+
val get_error : Proto_invocation.t -> Proto_error.method_error option
37
39
(** [get_error invocation] returns the error if this is an error response. *)
+10
-10
proto/session.ml
lib/proto/proto_session.ml
+10
-10
proto/session.ml
lib/proto/proto_session.ml
···
25
25
|> Jsont.Object.mem "name" Jsont.string ~enc:name
26
26
|> Jsont.Object.mem "isPersonal" Jsont.bool ~enc:is_personal
27
27
|> Jsont.Object.mem "isReadOnly" Jsont.bool ~enc:is_read_only
28
-
|> Jsont.Object.mem "accountCapabilities" (Json_map.of_string Jsont.json) ~enc:account_capabilities
28
+
|> Jsont.Object.mem "accountCapabilities" (Proto_json_map.of_string Jsont.json) ~enc:account_capabilities
29
29
|> Jsont.Object.finish
30
30
end
31
31
32
32
type t = {
33
33
capabilities : (string * Jsont.json) list;
34
-
accounts : (Id.t * Account.t) list;
35
-
primary_accounts : (string * Id.t) list;
34
+
accounts : (Proto_id.t * Account.t) list;
35
+
primary_accounts : (string * Proto_id.t) list;
36
36
username : string;
37
37
api_url : string;
38
38
download_url : string;
···
59
59
let jsont =
60
60
let kind = "Session" in
61
61
Jsont.Object.map ~kind make
62
-
|> Jsont.Object.mem "capabilities" (Json_map.of_string Jsont.json) ~enc:capabilities
63
-
|> Jsont.Object.mem "accounts" (Json_map.of_id Account.jsont) ~enc:accounts
64
-
|> Jsont.Object.mem "primaryAccounts" (Json_map.of_string Id.jsont) ~enc:primary_accounts
62
+
|> Jsont.Object.mem "capabilities" (Proto_json_map.of_string Jsont.json) ~enc:capabilities
63
+
|> Jsont.Object.mem "accounts" (Proto_json_map.of_id Account.jsont) ~enc:accounts
64
+
|> Jsont.Object.mem "primaryAccounts" (Proto_json_map.of_string Proto_id.jsont) ~enc:primary_accounts
65
65
|> Jsont.Object.mem "username" Jsont.string ~enc:username
66
66
|> Jsont.Object.mem "apiUrl" Jsont.string ~enc:api_url
67
67
|> Jsont.Object.mem "downloadUrl" Jsont.string ~enc:download_url
···
80
80
List.exists (fun (k, _) -> k = uri) session.capabilities
81
81
82
82
let get_core_capability session =
83
-
match List.assoc_opt Capability.core session.capabilities with
83
+
match List.assoc_opt Proto_capability.core session.capabilities with
84
84
| None -> None
85
85
| Some json ->
86
-
(match Jsont.Json.decode' Capability.Core.jsont json with
86
+
(match Jsont.Json.decode' Proto_capability.Core.jsont json with
87
87
| Ok v -> Some v
88
88
| Error _ -> None)
89
89
90
90
let get_mail_capability session =
91
-
match List.assoc_opt Capability.mail session.capabilities with
91
+
match List.assoc_opt Proto_capability.mail session.capabilities with
92
92
| None -> None
93
93
| Some json ->
94
-
(match Jsont.Json.decode' Capability.Mail.jsont json with
94
+
(match Jsont.Json.decode' Proto_capability.Mail.jsont json with
95
95
| Ok v -> Some v
96
96
| Error _ -> None)
+11
-9
proto/session.mli
lib/proto/proto_session.mli
+11
-9
proto/session.mli
lib/proto/proto_session.mli
···
3
3
SPDX-License-Identifier: ISC
4
4
---------------------------------------------------------------------------*)
5
5
6
-
(** JMAP session object as defined in RFC 8620 Section 2 *)
6
+
(** JMAP session object as defined in RFC 8620 Section 2
7
+
8
+
@canonical Jmap.Proto.Session *)
7
9
8
10
(** {1 Account} *)
9
11
···
34
36
type t = {
35
37
capabilities : (string * Jsont.json) list;
36
38
(** Server capabilities. Keys are capability URIs. *)
37
-
accounts : (Id.t * Account.t) list;
39
+
accounts : (Proto_id.t * Account.t) list;
38
40
(** Available accounts keyed by account id. *)
39
-
primary_accounts : (string * Id.t) list;
41
+
primary_accounts : (string * Proto_id.t) list;
40
42
(** Map of capability URI to the primary account id for that capability. *)
41
43
username : string;
42
44
(** The username associated with the credentials. *)
···
53
55
}
54
56
55
57
val capabilities : t -> (string * Jsont.json) list
56
-
val accounts : t -> (Id.t * Account.t) list
57
-
val primary_accounts : t -> (string * Id.t) list
58
+
val accounts : t -> (Proto_id.t * Account.t) list
59
+
val primary_accounts : t -> (string * Proto_id.t) list
58
60
val username : t -> string
59
61
val api_url : t -> string
60
62
val download_url : t -> string
···
67
69
68
70
(** {1 Session Helpers} *)
69
71
70
-
val get_account : Id.t -> t -> Account.t option
72
+
val get_account : Proto_id.t -> t -> Account.t option
71
73
(** [get_account id session] returns the account with the given id. *)
72
74
73
-
val primary_account_for : string -> t -> Id.t option
75
+
val primary_account_for : string -> t -> Proto_id.t option
74
76
(** [primary_account_for capability session] returns the primary account
75
77
for the given capability URI. *)
76
78
77
79
val has_capability : string -> t -> bool
78
80
(** [has_capability uri session] returns [true] if the server supports the capability. *)
79
81
80
-
val get_core_capability : t -> Capability.Core.t option
82
+
val get_core_capability : t -> Proto_capability.Core.t option
81
83
(** [get_core_capability session] returns the parsed core capability. *)
82
84
83
-
val get_mail_capability : t -> Capability.Mail.t option
85
+
val get_mail_capability : t -> Proto_capability.Mail.t option
84
86
(** [get_mail_capability session] returns the parsed mail capability. *)
proto/unknown.ml
lib/proto/proto_unknown.ml
proto/unknown.ml
lib/proto/proto_unknown.ml
+3
-1
proto/unknown.mli
lib/proto/proto_unknown.mli
+3
-1
proto/unknown.mli
lib/proto/proto_unknown.mli
···
6
6
(** Unknown field preservation for forward compatibility.
7
7
8
8
All JMAP objects preserve unknown fields to support future spec versions
9
-
and custom extensions. *)
9
+
and custom extensions.
10
+
11
+
@canonical Jmap.Proto.Unknown *)
10
12
11
13
type t = Jsont.json
12
14
(** Unknown or unrecognized JSON object members as a generic JSON value.
+1
-1
test/proto/dune
+1
-1
test/proto/dune
+24
-24
test/proto/test_proto.ml
+24
-24
test/proto/test_proto.ml
···
50
50
51
51
(* ID tests *)
52
52
module Id_tests = struct
53
-
open Jmap_proto
53
+
open Jmap.Proto
54
54
55
55
let test_valid_simple () =
56
56
let json = "\"abc123\"" in
···
130
130
131
131
(* Int53 tests *)
132
132
module Int53_tests = struct
133
-
open Jmap_proto
133
+
open Jmap.Proto
134
134
135
135
let test_zero () =
136
136
match decode Int53.Signed.jsont "0" with
···
198
198
199
199
(* Date tests *)
200
200
module Date_tests = struct
201
-
open Jmap_proto
201
+
open Jmap.Proto
202
202
203
203
let test_utc_z () =
204
204
match decode Date.Utc.jsont "\"2024-01-15T10:30:00Z\"" with
···
236
236
237
237
(* Session tests *)
238
238
module Session_tests = struct
239
-
open Jmap_proto
239
+
open Jmap.Proto
240
240
241
241
let test_minimal () =
242
242
test_decode_success "minimal session" Session.jsont "session/valid/minimal.json" ()
···
285
285
286
286
(* Request tests *)
287
287
module Request_tests = struct
288
-
open Jmap_proto
288
+
open Jmap.Proto
289
289
290
290
let test_single_method () =
291
291
test_decode_success "single method" Request.jsont "request/valid/single_method.json" ()
···
322
322
323
323
(* Response tests *)
324
324
module Response_tests = struct
325
-
open Jmap_proto
325
+
open Jmap.Proto
326
326
327
327
let test_success () =
328
328
test_decode_success "success" Response.jsont "response/valid/success.json" ()
···
359
359
360
360
(* Invocation tests *)
361
361
module Invocation_tests = struct
362
-
open Jmap_proto
362
+
open Jmap.Proto
363
363
364
364
let test_get () =
365
365
test_decode_success "get" Invocation.jsont "invocation/valid/get.json" ()
···
396
396
397
397
(* Capability tests *)
398
398
module Capability_tests = struct
399
-
open Jmap_proto
399
+
open Jmap.Proto
400
400
401
401
let test_core () =
402
402
test_decode_success "core" Capability.Core.jsont "capability/valid/core.json" ()
···
435
435
436
436
(* Method args/response tests *)
437
437
module Method_tests = struct
438
-
open Jmap_proto
438
+
open Jmap.Proto
439
439
440
440
let test_get_args () =
441
441
test_decode_success "get_args" Method.get_args_jsont "method/valid/get_args.json" ()
···
491
491
492
492
(* Error tests *)
493
493
module Error_tests = struct
494
-
open Jmap_proto
494
+
open Jmap.Proto
495
495
496
496
let test_method_error () =
497
497
test_decode_success "method_error" Error.method_error_jsont "error/valid/method_error.json" ()
···
589
589
590
590
(* Mailbox tests *)
591
591
module Mailbox_tests = struct
592
-
open Jmap_mail
592
+
open Jmap.Proto
593
593
594
594
let role_testable =
595
595
Alcotest.testable
···
607
607
match decode Mailbox.jsont json with
608
608
| Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e)
609
609
| Ok mb ->
610
-
Alcotest.(check string) "id" "mb1" (Jmap_proto.Id.to_string (Mailbox.id mb));
610
+
Alcotest.(check string) "id" "mb1" (Jmap.Proto.Id.to_string (Mailbox.id mb));
611
611
Alcotest.(check string) "name" "Inbox" (Mailbox.name mb);
612
612
Alcotest.(check (option role_testable)) "role" (Some Mailbox.Inbox) (Mailbox.role mb);
613
613
Alcotest.(check int64) "totalEmails" 150L (Mailbox.total_emails mb);
···
643
643
644
644
(* Email tests *)
645
645
module Email_tests = struct
646
-
open Jmap_mail
646
+
open Jmap.Proto
647
647
648
648
let test_minimal () =
649
649
test_decode_success "minimal" Email.jsont "mail/email/valid/minimal.json" ()
···
659
659
match decode Email.jsont json with
660
660
| Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e)
661
661
| Ok email ->
662
-
Alcotest.(check string) "id" "e1" (Jmap_proto.Id.to_string (Email.id email));
663
-
Alcotest.(check string) "blobId" "blob1" (Jmap_proto.Id.to_string (Email.blob_id email));
662
+
Alcotest.(check string) "id" "e1" (Jmap.Proto.Id.to_string (Email.id email));
663
+
Alcotest.(check string) "blobId" "blob1" (Jmap.Proto.Id.to_string (Email.blob_id email));
664
664
Alcotest.(check int64) "size" 1024L (Email.size email)
665
665
666
666
let test_full_values () =
···
734
734
735
735
(* Thread tests *)
736
736
module Thread_tests = struct
737
-
open Jmap_mail
737
+
open Jmap.Proto
738
738
739
739
let test_simple () =
740
740
test_decode_success "simple" Thread.jsont "mail/thread/valid/simple.json" ()
···
747
747
match decode Thread.jsont json with
748
748
| Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e)
749
749
| Ok thread ->
750
-
Alcotest.(check string) "id" "t2" (Jmap_proto.Id.to_string (Thread.id thread));
750
+
Alcotest.(check string) "id" "t2" (Jmap.Proto.Id.to_string (Thread.id thread));
751
751
Alcotest.(check int) "emailIds count" 5 (List.length (Thread.email_ids thread))
752
752
753
753
let tests = [
···
759
759
760
760
(* Identity tests *)
761
761
module Identity_tests = struct
762
-
open Jmap_mail
762
+
open Jmap.Proto
763
763
764
764
let test_simple () =
765
765
test_decode_success "simple" Identity.jsont "mail/identity/valid/simple.json" ()
···
781
781
782
782
(* Email address tests *)
783
783
module Email_address_tests = struct
784
-
open Jmap_mail
784
+
open Jmap.Proto
785
785
786
786
let test_full () =
787
787
test_decode_success "full" Email_address.jsont "mail/email_address/valid/full.json" ()
···
815
815
816
816
(* Vacation tests *)
817
817
module Vacation_tests = struct
818
-
open Jmap_mail
818
+
open Jmap.Proto
819
819
820
820
let test_enabled () =
821
821
test_decode_success "enabled" Vacation.jsont "mail/vacation/valid/enabled.json" ()
···
849
849
850
850
(* Comparator tests *)
851
851
module Comparator_tests = struct
852
-
open Jmap_proto
852
+
open Jmap.Proto
853
853
854
854
let test_minimal () =
855
855
test_decode_success "minimal" Filter.comparator_jsont "filter/valid/comparator_minimal.json" ()
···
888
888
889
889
(* EmailBody tests *)
890
890
module EmailBody_tests = struct
891
-
open Jmap_mail
891
+
open Jmap.Proto
892
892
893
893
let test_text_part () =
894
894
test_decode_success "text part" Email_body.Part.jsont "mail/email_body/valid/text_part.json" ()
···
932
932
933
933
(* EmailSubmission tests *)
934
934
module EmailSubmission_tests = struct
935
-
open Jmap_mail
935
+
open Jmap.Proto
936
936
937
937
let test_simple () =
938
938
test_decode_success "simple" Submission.jsont "mail/submission/valid/simple.json" ()
···
948
948
match decode Submission.jsont json with
949
949
| Error e -> Alcotest.failf "decode failed: %s" (Jsont.Error.to_string e)
950
950
| Ok sub ->
951
-
Alcotest.(check string) "id" "sub1" (Jmap_proto.Id.to_string (Submission.id sub));
951
+
Alcotest.(check string) "id" "sub1" (Jmap.Proto.Id.to_string (Submission.id sub));
952
952
(* Check undoStatus is Pending *)
953
953
match Submission.undo_status sub with
954
954
| Submission.Pending -> ()