this repo has no description

reorg around a single Jmap module

+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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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 (library 2 2 (name jmap_eio) 3 3 (public_name jmap-eio) 4 - (libraries jmap jmap.mail jsont jsont.bytesrw eio requests uri str cmdliner fmt.tty) 4 + (libraries jmap jsont jsont.bytesrw eio requests uri str cmdliner fmt.tty) 5 5 (modules jmap_eio codec client cli))
+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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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 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
+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
··· 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
+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
··· 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
··· 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
+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
+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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 1 - (library 2 - (name jmap_mail) 3 - (public_name jmap.mail) 4 - (libraries jmap jsont ptime) 5 - (modules 6 - jmap_mail 7 - email_address 8 - email_header 9 - email_body 10 - mailbox 11 - thread 12 - email 13 - search_snippet 14 - identity 15 - submission 16 - vacation 17 - mail_filter))
+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
··· 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
+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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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 ---------------------------------------------------------------------------*) 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
+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 2 2 (name test_proto) 3 3 (package jmap) 4 - (libraries jmap jmap.mail alcotest jsont.bytesrw) 4 + (libraries jmap alcotest jsont.bytesrw) 5 5 (deps 6 6 (source_tree id) 7 7 (source_tree int53)
+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 -> ()