My agentic slop goes here. Not intended for anyone else!
1(** JMAP Primitive Data Types
2
3 This module defines the primitive data types used in JMAP:
4 - Int (signed 53-bit integer)
5 - UnsignedInt (unsigned integer 0 to 2^53-1)
6 - Date (RFC 3339 date-time)
7 - UTCDate (RFC 3339 date-time with Z timezone)
8
9 Reference: RFC 8620 Section 1.3
10*)
11
12(** Signed 53-bit integer (-2^53 + 1 to 2^53 - 1)
13 JavaScript's safe integer range *)
14module Int53 = struct
15 type t = int
16
17 let min_value = -9007199254740991 (* -(2^53 - 1) *)
18 let max_value = 9007199254740991 (* 2^53 - 1 *)
19
20 let of_int i =
21 if i < min_value || i > max_value then
22 raise (Invalid_argument "Int53 out of range")
23 else
24 i
25
26 let to_int t = t
27
28 (** Parse from JSON.
29 Test files: test/data/core/request_query.json (position, anchorOffset) *)
30 let of_json = function
31 | `Float f ->
32 let i = int_of_float f in
33 if Float.is_integer f then
34 of_int i
35 else
36 raise (Jmap_error.Parse_error "Int53 must be an integer")
37 | _ -> raise (Jmap_error.Parse_error "Int53 must be a JSON number")
38
39 let to_json t = `Float (float_of_int t)
40end
41
42(** Unsigned integer (0 to 2^53 - 1) *)
43module UnsignedInt = struct
44 type t = int
45
46 let min_value = 0
47 let max_value = 9007199254740991 (* 2^53 - 1 *)
48
49 let of_int i =
50 if i < min_value || i > max_value then
51 raise (Invalid_argument "UnsignedInt out of range")
52 else
53 i
54
55 let to_int t = t
56
57 (** Parse from JSON.
58 Test files:
59 - test/data/mail/mailbox_get_response.json (totalEmails, unreadEmails, etc.)
60 - test/data/core/request_query.json (limit)
61 *)
62 let of_json = function
63 | `Float f ->
64 let i = int_of_float f in
65 if Float.is_integer f && i >= 0 then
66 of_int i
67 else
68 raise (Jmap_error.Parse_error "UnsignedInt must be a non-negative integer")
69 | _ -> raise (Jmap_error.Parse_error "UnsignedInt must be a JSON number")
70
71 let to_json t = `Float (float_of_int t)
72end
73
74(** RFC 3339 date-time (with or without timezone)
75 Examples: "2014-10-30T14:12:00+08:00", "2014-10-30T06:12:00Z"
76*)
77module Date = struct
78 type t = string
79
80 (** Basic validation of RFC 3339 format *)
81 let validate s =
82 (* Simple check: contains 'T' and has reasonable length *)
83 String.contains s 'T' && String.length s >= 19
84
85 let of_string s =
86 if validate s then s
87 else raise (Invalid_argument "Invalid RFC 3339 date-time format")
88
89 let to_string t = t
90
91 (** Parse from JSON.
92 Test files: test/data/mail/email_get_response.json (sentAt field) *)
93 let of_json = function
94 | `String s -> of_string s
95 | _ -> raise (Jmap_error.Parse_error "Date must be a JSON string")
96
97 let to_json t = `String t
98end
99
100(** RFC 3339 date-time with Z timezone (UTC)
101 Example: "2014-10-30T06:12:00Z"
102
103 MUST have "Z" suffix to indicate UTC.
104*)
105module UTCDate = struct
106 type t = string
107
108 (** Validate that string is RFC 3339 with Z suffix *)
109 let validate s =
110 String.contains s 'T' &&
111 String.length s >= 20 &&
112 s.[String.length s - 1] = 'Z'
113
114 let of_string s =
115 if validate s then s
116 else raise (Invalid_argument "Invalid RFC 3339 UTCDate format (must end with Z)")
117
118 let to_string t = t
119
120 (** Parse from JSON.
121 Test files:
122 - test/data/mail/email_get_response.json (receivedAt field)
123 - test/data/mail/email_submission_get_response.json (sendAt field)
124 *)
125 let of_json = function
126 | `String s -> of_string s
127 | _ -> raise (Jmap_error.Parse_error "UTCDate must be a JSON string")
128
129 let to_json t = `String t
130
131 (** Get current UTC time as UTCDate *)
132 let now () =
133 let open Unix in
134 let tm = gmtime (time ()) in
135 Printf.sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ"
136 (tm.tm_year + 1900)
137 (tm.tm_mon + 1)
138 tm.tm_mday
139 tm.tm_hour
140 tm.tm_min
141 tm.tm_sec
142end