this repo has no description

Add typed capability handling for JMAP URIs

- Create Capability module in Jmap with typed handling of core URIs
- Create Capability module in Jmap_mail with typed handling of mail URIs
- Replace hardcoded URI strings with typed versions in client code
- Improves type safety by ensuring URIs are correctly managed
- Provides parsing/stringifying functions for interoperability

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>

+2 -1
bin/fastmail_list.ml
···
| Ok conn ->
(* Get the primary account ID *)
let primary_account_id =
-
match List.assoc_opt "urn:ietf:params:jmap:mail" conn.session.primary_accounts with
+
let mail_capability = Jmap_mail.Capability.to_string (Jmap_mail.Capability.Mail Mail) in
+
match List.assoc_opt mail_capability conn.session.primary_accounts with
| Some id -> id
| None ->
match conn.session.accounts with
+49
lib/jmap.ml
···
* https://datatracker.ietf.org/doc/html/rfc8620
*)
+
(** Module for managing JMAP capability URIs and other constants *)
+
module Capability = struct
+
(** Core JMAP capability URI *)
+
type core = Core
+
+
(** JMAP capability URI as specified in RFC8620 *)
+
let core_uri = "urn:ietf:params:jmap:core"
+
+
(** Convert core capability to URI string *)
+
let string_of_core = function
+
| Core -> core_uri
+
+
(** Parse a string to a core capability, returns None if not a valid capability URI *)
+
let core_of_string = function
+
| s when s = core_uri -> Some Core
+
| _ -> None
+
+
(** All JMAP capability types *)
+
type t =
+
| Core of core
+
| Extension of string
+
+
(** Convert capability to URI string *)
+
let to_string = function
+
| Core c -> string_of_core c
+
| Extension s -> s
+
+
(** Parse a string to a capability, returns Extension for non-core capabilities *)
+
let of_string s =
+
match core_of_string s with
+
| Some c -> Core c
+
| None -> Extension s
+
+
(** Check if a capability matches a core capability *)
+
let is_core = function
+
| Core _ -> true
+
| Extension _ -> false
+
+
(** Check if a capability string is a core capability *)
+
let is_core_string s =
+
match of_string s with
+
| Core _ -> true
+
| Extension _ -> false
+
+
(** Create a list of capability strings *)
+
let strings_of_capabilities capabilities =
+
List.map to_string capabilities
+
end
+
module Types = struct
(** Id string as per Section 1.2 *)
type id = string
+35
lib/jmap.mli
···
* https://datatracker.ietf.org/doc/html/rfc8620
*)
+
(** Module for managing JMAP capability URIs and other constants *)
+
module Capability : sig
+
(** Core JMAP capability URI *)
+
type core = Core
+
+
(** JMAP capability URI as specified in RFC8620 *)
+
val core_uri : string
+
+
(** Convert core capability to URI string *)
+
val string_of_core : core -> string
+
+
(** Parse a string to a core capability, returns None if not a valid capability URI *)
+
val core_of_string : string -> core option
+
+
(** All JMAP capability types *)
+
type t =
+
| Core of core
+
| Extension of string
+
+
(** Convert capability to URI string *)
+
val to_string : t -> string
+
+
(** Parse a string to a capability, returns Extension for non-core capabilities *)
+
val of_string : string -> t
+
+
(** Check if a capability matches a core capability *)
+
val is_core : t -> bool
+
+
(** Check if a capability string is a core capability *)
+
val is_core_string : string -> bool
+
+
(** Create a list of capability strings *)
+
val strings_of_capabilities : t list -> string list
+
end
+
(** {1 Types} *)
module Types : sig
+112 -8
lib/jmap_mail.ml
···
(** Implementation of the JMAP Mail extension, as defined in RFC8621 *)
+
(** Module for managing JMAP Mail-specific capability URIs *)
+
module Capability = struct
+
(** Mail capability types *)
+
type mail = Mail
+
+
(** Mail capability URI *)
+
let mail_uri = "urn:ietf:params:jmap:mail"
+
+
(** Convert mail capability to URI string *)
+
let string_of_mail = function
+
| Mail -> mail_uri
+
+
(** Parse a string to mail capability *)
+
let mail_of_string = function
+
| s when s = mail_uri -> Some Mail
+
| _ -> None
+
+
(** Submission capability types *)
+
type submission = Submission
+
+
(** Submission capability URI *)
+
let submission_uri = "urn:ietf:params:jmap:submission"
+
+
(** Convert submission capability to URI string *)
+
let string_of_submission = function
+
| Submission -> submission_uri
+
+
(** Parse a string to submission capability *)
+
let submission_of_string = function
+
| s when s = submission_uri -> Some Submission
+
| _ -> None
+
+
(** Vacation response capability types *)
+
type vacation_response = VacationResponse
+
+
(** Vacation response capability URI *)
+
let vacation_response_uri = "urn:ietf:params:jmap:vacationresponse"
+
+
(** Convert vacation response capability to URI string *)
+
let string_of_vacation_response = function
+
| VacationResponse -> vacation_response_uri
+
+
(** Parse a string to vacation response capability *)
+
let vacation_response_of_string = function
+
| s when s = vacation_response_uri -> Some VacationResponse
+
| _ -> None
+
+
(** All mail extension capability types *)
+
type t =
+
| Mail of mail
+
| Submission of submission
+
| VacationResponse of vacation_response
+
| Extension of string
+
+
(** Convert capability to URI string *)
+
let to_string = function
+
| Mail m -> string_of_mail m
+
| Submission s -> string_of_submission s
+
| VacationResponse v -> string_of_vacation_response v
+
| Extension s -> s
+
+
(** Parse a string to a capability *)
+
let of_string s =
+
match mail_of_string s with
+
| Some m -> Mail m
+
| None ->
+
match submission_of_string s with
+
| Some s -> Submission s
+
| None ->
+
match vacation_response_of_string s with
+
| Some v -> VacationResponse v
+
| None -> Extension s
+
+
(** Check if a capability is a standard mail capability *)
+
let is_standard = function
+
| Mail _ | Submission _ | VacationResponse _ -> true
+
| Extension _ -> false
+
+
(** Check if a capability string is a standard mail capability *)
+
let is_standard_string s =
+
match of_string s with
+
| Extension _ -> false
+
| _ -> true
+
+
(** Create a list of capability strings *)
+
let strings_of_capabilities capabilities =
+
List.map to_string capabilities
+
end
+
module Types = struct
open Jmap.Types
(** {1 Mail capabilities} *)
(** Capability URI for JMAP Mail*)
-
let capability_mail = "urn:ietf:params:jmap:mail"
+
let capability_mail = Capability.mail_uri
(** Capability URI for JMAP Submission *)
-
let capability_submission = "urn:ietf:params:jmap:submission"
+
let capability_submission = Capability.submission_uri
(** Capability URI for JMAP Vacation Response *)
-
let capability_vacation_response = "urn:ietf:params:jmap:vacationresponse"
+
let capability_vacation_response = Capability.vacation_response_uri
(** {1:mailbox Mailbox objects} *)
···
TODO:claude *)
let get_mailboxes conn ~account_id =
let request = {
-
using = ["urn:ietf:params:jmap:core"; Types.capability_mail];
+
using = [
+
Jmap.Capability.to_string (Jmap.Capability.Core Core);
+
Capability.to_string (Capability.Mail Mail)
+
];
method_calls = [
name = "Mailbox/get";
···
TODO:claude *)
let get_mailbox conn ~account_id ~mailbox_id =
let request = {
-
using = ["urn:ietf:params:jmap:core"; Types.capability_mail];
+
using = [
+
Jmap.Capability.to_string (Jmap.Capability.Core Core);
+
Capability.to_string (Capability.Mail Mail)
+
];
method_calls = [
name = "Mailbox/get";
···
let get_messages_in_mailbox conn ~account_id ~mailbox_id ?limit () =
(* First query the emails in the mailbox *)
let query_request = {
-
using = ["urn:ietf:params:jmap:core"; Types.capability_mail];
+
using = [
+
Jmap.Capability.to_string (Jmap.Capability.Core Core);
+
Capability.to_string (Capability.Mail Mail)
+
];
method_calls = [
name = "Email/query";
···
(* If we have IDs, fetch the actual email objects *)
if List.length email_ids > 0 then
let get_request = {
-
using = ["urn:ietf:params:jmap:core"; Types.capability_mail];
+
using = [
+
Jmap.Capability.to_string (Jmap.Capability.Core Core);
+
Capability.to_string (Capability.Mail Mail)
+
];
method_calls = [
name = "Email/get";
···
TODO:claude *)
let get_email conn ~account_id ~email_id =
let request = {
-
using = ["urn:ietf:params:jmap:core"; Types.capability_mail];
+
using = [
+
Jmap.Capability.to_string (Jmap.Capability.Core Core);
+
Capability.to_string (Capability.Mail Mail)
+
];
method_calls = [
name = "Email/get";
+61
lib/jmap_mail.mli
···
(** Implementation of the JMAP Mail extension, as defined in RFC8621 *)
+
(** Module for managing JMAP Mail-specific capability URIs *)
+
module Capability : sig
+
(** Mail capability types *)
+
type mail = Mail
+
+
(** Mail capability URI *)
+
val mail_uri : string
+
+
(** Convert mail capability to URI string *)
+
val string_of_mail : mail -> string
+
+
(** Parse a string to mail capability *)
+
val mail_of_string : string -> mail option
+
+
(** Submission capability types *)
+
type submission = Submission
+
+
(** Submission capability URI *)
+
val submission_uri : string
+
+
(** Convert submission capability to URI string *)
+
val string_of_submission : submission -> string
+
+
(** Parse a string to submission capability *)
+
val submission_of_string : string -> submission option
+
+
(** Vacation response capability types *)
+
type vacation_response = VacationResponse
+
+
(** Vacation response capability URI *)
+
val vacation_response_uri : string
+
+
(** Convert vacation response capability to URI string *)
+
val string_of_vacation_response : vacation_response -> string
+
+
(** Parse a string to vacation response capability *)
+
val vacation_response_of_string : string -> vacation_response option
+
+
(** All mail extension capability types *)
+
type t =
+
| Mail of mail
+
| Submission of submission
+
| VacationResponse of vacation_response
+
| Extension of string
+
+
(** Convert capability to URI string *)
+
val to_string : t -> string
+
+
(** Parse a string to a capability *)
+
val of_string : string -> t
+
+
(** Check if a capability is a standard mail capability *)
+
val is_standard : t -> bool
+
+
(** Check if a capability string is a standard mail capability *)
+
val is_standard_string : string -> bool
+
+
(** Create a list of capability strings *)
+
val strings_of_capabilities : t list -> string list
+
end
+
(** Types for the JMAP Mail extension *)
module Types : sig
open Jmap.Types