···
1
+
(** JMAP Module Type Signatures.
3
+
This module defines the standard module type signatures used throughout
4
+
the JMAP implementation. These signatures ensure consistency across all
5
+
JMAP types and provide a discoverable interface for developers.
7
+
All wire protocol types, data objects, and method arguments/responses
8
+
should conform to these signatures as appropriate.
10
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html> RFC 8620 - The JSON Meta Application Protocol (JMAP)
11
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html> RFC 8621 - JMAP Mail *)
13
+
(** {1 Core Signatures} *)
15
+
(** Signature for types that can be serialized to/from JSON.
17
+
This is the fundamental signature for any type that needs to be
18
+
transmitted over the JMAP wire protocol or stored as JSON. *)
19
+
module type JSONABLE = sig
22
+
(** Convert to JSON representation.
23
+
@return JSON representation suitable for wire transmission *)
24
+
val to_json : t -> Yojson.Safe.t
26
+
(** Parse from JSON representation.
27
+
@param json The JSON value to parse
28
+
@return The parsed value
29
+
@raise Failure if JSON structure is invalid or required fields are missing *)
30
+
val of_json : Yojson.Safe.t -> t
33
+
(** Signature for types that can be pretty-printed using Fmt.
35
+
This provides composable formatting for debugging, logging, and
36
+
human-readable output. Using Fmt allows for better integration
37
+
with logging libraries and testing frameworks. *)
38
+
module type PRINTABLE = sig
41
+
(** Pretty-printer for the type.
42
+
@param ppf The formatter to write to
43
+
@param t The value to print *)
44
+
val pp : Format.formatter -> t -> unit
46
+
(** Alternative name for pp, following Fmt conventions *)
47
+
val pp_hum : Format.formatter -> t -> unit
50
+
(** {1 Wire Protocol Signatures} *)
52
+
(** Signature for JMAP wire protocol types.
54
+
Types that travel over HTTP as part of the JMAP protocol should
55
+
implement this signature. This includes requests, responses, and
56
+
all their component parts. *)
57
+
module type WIRE_TYPE = sig
60
+
include JSONABLE with type t := t
61
+
include PRINTABLE with type t := t
63
+
(** Validate the structure according to JMAP constraints.
64
+
@return Ok () if valid, Error with description if invalid *)
65
+
val validate : t -> (unit, string) result
68
+
(** {1 JMAP Object Signatures} *)
70
+
(** Signature for JMAP data objects.
72
+
This signature is for the core JMAP data types like Email, Mailbox,
73
+
Thread, Identity, etc. These objects have IDs and support property
74
+
selection for efficient data transfer. *)
75
+
module type JMAP_OBJECT = sig
77
+
type id_type = string (* Jmap_types.id *)
79
+
include JSONABLE with type t := t
80
+
include PRINTABLE with type t := t
82
+
(** Get the object's identifier.
83
+
@return The object ID if present (may be None for unsaved objects) *)
84
+
val id : t -> id_type option
86
+
(** Create a minimal valid object.
87
+
@param id Optional identifier for the object
88
+
@return A new object with default/empty values for optional fields *)
89
+
val create : ?id:id_type -> unit -> t
91
+
(** Serialize to JSON with only specified properties.
93
+
This is used to implement the JMAP properties selection mechanism,
94
+
allowing clients to request only the fields they need.
96
+
@param properties List of property names to include
97
+
@param t The object to serialize
98
+
@return JSON with only the requested properties *)
99
+
val to_json_with_properties : properties:string list -> t -> Yojson.Safe.t
101
+
(** Get the list of all valid property names for this object type.
102
+
@return List of property names that can be requested *)
103
+
val valid_properties : unit -> string list
106
+
(** {1 Method Signatures} *)
108
+
(** Signature for JMAP method argument types.
110
+
All JMAP method calls take an arguments object. This signature
111
+
ensures consistency across all method argument types. *)
112
+
module type METHOD_ARGS = sig
114
+
type account_id = string (* Jmap_types.id *)
116
+
include JSONABLE with type t := t
117
+
include PRINTABLE with type t := t
119
+
(** Get the account ID these arguments apply to.
120
+
@return The account ID for this method call *)
121
+
val account_id : t -> account_id
123
+
(** Validate arguments according to JMAP method constraints.
124
+
@return Ok () if valid, Error with description if invalid *)
125
+
val validate : t -> (unit, string) result
127
+
(** Get the method name these arguments are for.
128
+
@return The JMAP method name (e.g., "Email/get") *)
129
+
val method_name : unit -> string
132
+
(** Signature for JMAP method response types.
134
+
All JMAP method responses follow a similar pattern with account IDs
135
+
and state tokens for synchronization. *)
136
+
module type METHOD_RESPONSE = sig
138
+
type account_id = string (* Jmap_types.id *)
139
+
type state = string
141
+
include JSONABLE with type t := t
142
+
include PRINTABLE with type t := t
144
+
(** Get the account ID this response applies to.
145
+
@return The account ID for this response *)
146
+
val account_id : t -> account_id
148
+
(** Get the state token for synchronization.
149
+
@return The state token if present (used for changes/updates) *)
150
+
val state : t -> state option
152
+
(** Check if this response indicates an error condition.
153
+
@return true if this is an error response *)
154
+
val is_error : t -> bool
157
+
(** {1 Collection Signatures} *)
159
+
(** Signature for types that represent collections of JMAP objects.
161
+
This is used for query results, batch operations, and any other
162
+
operation that deals with multiple objects. *)
163
+
module type COLLECTION = sig
167
+
include JSONABLE with type t := t
168
+
include PRINTABLE with type t := t
170
+
(** Get the items in the collection.
171
+
@return List of items in the collection *)
172
+
val items : t -> item list
174
+
(** Get the total count of items (may be different from length of items).
175
+
@return Total count if known *)
176
+
val total : t -> int option
178
+
(** Create a collection from a list of items.
179
+
@param items The items to include
180
+
@param total Optional total count (for paginated results)
181
+
@return A new collection *)
182
+
val create : items:item list -> ?total:int -> unit -> t
184
+
(** Map a function over the items in the collection.
185
+
@param f Function to apply to each item
186
+
@return New collection with transformed items *)
187
+
val map : (item -> item) -> t -> t
189
+
(** Filter items in the collection.
190
+
@param f Predicate function
191
+
@return New collection with only items where f returns true *)
192
+
val filter : (item -> bool) -> t -> t
195
+
(** {1 Error Handling Signatures} *)
197
+
(** Signature for JMAP error types.
199
+
JMAP has a structured error model with specific error codes
200
+
and optional additional properties. *)
201
+
module type ERROR_TYPE = sig
204
+
include JSONABLE with type t := t
205
+
include PRINTABLE with type t := t
207
+
(** Get the JMAP error type string.
208
+
@return The error type (e.g., "accountNotFound", "invalidArguments") *)
209
+
val error_type : t -> string
211
+
(** Get the human-readable error description.
212
+
@return Optional description of the error *)
213
+
val description : t -> string option
215
+
(** Create an error with the given type and description.
216
+
@param error_type The JMAP error type
217
+
@param description Optional human-readable description
218
+
@return A new error *)
219
+
val create : error_type:string -> ?description:string -> unit -> t
222
+
(** {1 RFC Compliance Signatures} *)
224
+
(** Signature for types that implement specific RFC sections.
226
+
This provides metadata about which parts of the JMAP RFCs
227
+
are implemented by each type, making it easier to track
228
+
compliance and find documentation. *)
229
+
module type RFC_COMPLIANT = sig
232
+
(** Get the RFC section this type implements.
233
+
@return RFC section reference (e.g., "RFC 8620, Section 5.1") *)
234
+
val rfc_section : unit -> string
236
+
(** Get the URL to the RFC section.
237
+
@return Direct URL to the RFC section *)
238
+
val rfc_url : unit -> string
240
+
(** Get implementation notes specific to this OCaml binding.
241
+
@return List of implementation notes and decisions *)
242
+
val implementation_notes : unit -> string list
244
+
(** Check if this implementation is complete.
245
+
@return true if all required RFC features are implemented *)
246
+
val is_complete : unit -> bool
248
+
(** Get list of unimplemented features from the RFC.
249
+
@return List of features not yet implemented *)
250
+
val unimplemented_features : unit -> string list
253
+
(** {1 Vendor Extension Signatures} *)
255
+
(** Signature for vendor-specific extensions to JMAP.
257
+
Vendors like Fastmail, Cyrus, etc. may add custom properties
258
+
or methods. This signature helps track these extensions. *)
259
+
module type VENDOR_EXTENSION = sig
262
+
include JSONABLE with type t := t
263
+
include PRINTABLE with type t := t
265
+
(** Get the vendor namespace.
266
+
@return The vendor identifier (e.g., "com.fastmail") *)
267
+
val vendor : unit -> string
269
+
(** Get the extension name.
270
+
@return The name of this extension *)
271
+
val extension_name : unit -> string
273
+
(** Get the capability URI for this extension.
274
+
@return The capability URI if this adds a new capability *)
275
+
val capability_uri : unit -> string option
277
+
(** Check if this extension is experimental.
278
+
@return true if this is an experimental/unstable extension *)
279
+
val is_experimental : unit -> bool
282
+
(** {1 Patch Operations Signatures} *)
284
+
(** Signature for types that support JSON Patch operations.
286
+
JMAP uses a subset of JSON Patch (RFC 6902) for partial updates.
287
+
This signature is for types that can generate or apply patches. *)
288
+
module type PATCHABLE = sig
292
+
include JSONABLE with type t := t
294
+
(** Create a patch to transform one value into another.
295
+
@param from The original value
296
+
@param to_ The target value
297
+
@return A patch that transforms from to to_ *)
298
+
val create_patch : from:t -> to_:t -> patch
300
+
(** Apply a patch to a value.
301
+
@param patch The patch to apply
302
+
@param t The value to patch
303
+
@return Ok with patched value or Error if patch cannot be applied *)
304
+
val apply_patch : patch:patch -> t -> (t, string) result
306
+
(** Convert a patch to JSON Pointer operations.
307
+
@param patch The patch to convert
308
+
@return List of (path, value) pairs for JSON Pointer operations *)
309
+
val patch_to_operations : patch -> (string * Yojson.Safe.t) list
312
+
(** {1 Composite Signatures} *)
314
+
(** Full signature for a complete JMAP object implementation.
316
+
This combines all the relevant signatures for a fully-featured
317
+
JMAP object that supports all standard operations. *)
318
+
module type FULL_JMAP_OBJECT = sig
319
+
include JMAP_OBJECT
320
+
include PATCHABLE with type t := t
321
+
include RFC_COMPLIANT with type t := t
324
+
(** Full signature for a complete method implementation.
326
+
This represents a complete JMAP method with both request and
327
+
response types, following all conventions. *)
328
+
module type JMAP_METHOD = sig
329
+
(** The method name (e.g., "Email/get") *)
330
+
val name : unit -> string
332
+
(** The request arguments type *)
333
+
module Args : METHOD_ARGS
335
+
(** The response type *)
336
+
module Response : METHOD_RESPONSE
338
+
(** Execute the method (client-side stub).
339
+
@param args The method arguments
340
+
@return A response or error *)
341
+
val execute : Args.t -> (Response.t, string) result