this repo has no description
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(** Email header types as defined in RFC 8621 Section 4.1.2
7
8 @canonical Jmap.Proto.Email_header *)
9
10(** {1 Raw Headers} *)
11
12(** A raw email header name-value pair. *)
13type t = {
14 name : string;
15 (** The header field name. *)
16 value : string;
17 (** The raw header field value. *)
18}
19
20val create : name:string -> value:string -> t
21
22val name : t -> string
23val value : t -> string
24
25val jsont : t Jsont.t
26
27(** {1 Header Categories}
28
29 RFC 8621 Section 4.1.2 restricts which parsed forms can be used with
30 which headers. These polymorphic variant types encode those restrictions
31 at the type level.
32
33 Each category corresponds to headers that share the same allowed forms:
34 - Address headers: can use [Addresses] and [Grouped_addresses] forms
35 - Message-ID headers: can use [Message_ids] form
36 - Date headers: can use [Date] form
37 - URL headers: can use [Urls] form
38 - Text headers: can use [Text] form
39 - All headers can use [Raw] form
40 - Custom headers (not in RFC 5322/2369) can use any form *)
41
42(** Headers that allow the [Addresses] and [Grouped_addresses] forms.
43 These are address-list headers per RFC 5322. *)
44type address_header = [
45 | `From
46 | `Sender
47 | `Reply_to
48 | `To
49 | `Cc
50 | `Bcc
51 | `Resent_from
52 | `Resent_sender
53 | `Resent_reply_to
54 | `Resent_to
55 | `Resent_cc
56 | `Resent_bcc
57]
58
59(** Headers that allow the [Message_ids] form.
60 These contain msg-id values per RFC 5322. *)
61type message_id_header = [
62 | `Message_id
63 | `In_reply_to
64 | `References
65 | `Resent_message_id
66]
67
68(** Headers that allow the [Date] form.
69 These contain date-time values per RFC 5322. *)
70type date_header = [
71 | `Date
72 | `Resent_date
73]
74
75(** Headers that allow the [Urls] form.
76 These are list-* headers per RFC 2369. *)
77type url_header = [
78 | `List_help
79 | `List_unsubscribe
80 | `List_subscribe
81 | `List_post
82 | `List_owner
83 | `List_archive
84]
85
86(** Headers that allow the [Text] form.
87 These contain unstructured or phrase content. *)
88type text_header = [
89 | `Subject
90 | `Comments
91 | `Keywords
92 | `List_id
93]
94
95(** All standard headers defined in RFC 5322 and RFC 2369. *)
96type standard_header = [
97 | address_header
98 | message_id_header
99 | date_header
100 | url_header
101 | text_header
102]
103
104(** A custom header not defined in RFC 5322 or RFC 2369.
105 Custom headers can use any parsed form. *)
106type custom_header = [ `Custom of string ]
107
108(** Any header - standard or custom. *)
109type any_header = [ standard_header | custom_header ]
110
111(** {2 Header Name Conversion} *)
112
113val standard_header_to_string : [< standard_header ] -> string
114(** Convert a standard header variant to its wire name (e.g., [`From] -> "From"). *)
115
116val standard_header_of_string : string -> standard_header option
117(** Parse a header name to a standard header variant, case-insensitive.
118 Returns [None] for non-standard headers. *)
119
120val any_header_to_string : [< any_header ] -> string
121(** Convert any header variant to its wire name. *)
122
123(** {1 Header Parsed Forms}
124
125 RFC 8621 defines several parsed forms for headers.
126 These can be requested via the [header:Name:form] properties. *)
127
128(** The parsed form to request for a header value. *)
129type form = [
130 | `Raw (** Raw octets, available for all headers *)
131 | `Text (** Decoded text, for text headers or custom *)
132 | `Addresses (** Flat address list, for address headers or custom *)
133 | `Grouped_addresses (** Address list with groups, for address headers or custom *)
134 | `Message_ids (** List of message-id strings, for message-id headers or custom *)
135 | `Date (** Parsed date, for date headers or custom *)
136 | `Urls (** List of URLs, for url headers or custom *)
137]
138
139val form_to_string : [< form ] -> string
140(** Convert form to wire suffix (e.g., [`Addresses] -> "asAddresses").
141 [`Raw] returns the empty string (raw is the default). *)
142
143val form_of_string : string -> form option
144(** Parse a form suffix (e.g., "asAddresses" -> [`Addresses]).
145 Empty string returns [`Raw]. *)
146
147(** {1 Header Property Requests}
148
149 Type-safe construction of [header:Name:form:all] property strings.
150 The GADT ensures that only valid form/header combinations are allowed. *)
151
152(** A header property request with type-safe form selection.
153
154 The type parameter encodes what forms are allowed:
155 - Address headers allow [Addresses] and [Grouped_addresses]
156 - Message-ID headers allow [Message_ids]
157 - Date headers allow [Date]
158 - URL headers allow [Urls]
159 - Text headers allow [Text]
160 - All headers allow [Raw]
161 - Custom headers allow any form *)
162type header_property =
163 | Raw of { name : string; all : bool }
164 (** Raw form, available for any header. *)
165
166 | Text of { header : [ text_header | custom_header ]; all : bool }
167 (** Text form, for text headers or custom. *)
168
169 | Addresses of { header : [ address_header | custom_header ]; all : bool }
170 (** Addresses form, for address headers or custom. *)
171
172 | Grouped_addresses of { header : [ address_header | custom_header ]; all : bool }
173 (** GroupedAddresses form, for address headers or custom. *)
174
175 | Message_ids of { header : [ message_id_header | custom_header ]; all : bool }
176 (** MessageIds form, for message-id headers or custom. *)
177
178 | Date of { header : [ date_header | custom_header ]; all : bool }
179 (** Date form, for date headers or custom. *)
180
181 | Urls of { header : [ url_header | custom_header ]; all : bool }
182 (** URLs form, for URL headers or custom. *)
183
184val header_property_to_string : header_property -> string
185(** Convert a header property request to wire format.
186 E.g., [Addresses { header = `From; all = true }] -> "header:From:asAddresses:all" *)
187
188val header_property_of_string : string -> header_property option
189(** Parse a header property string.
190 Returns [None] if the string doesn't match [header:*] format. *)
191
192(** {2 Convenience Constructors} *)
193
194val raw : ?all:bool -> string -> header_property
195(** [raw ?all name] creates a raw header property request. *)
196
197val text : ?all:bool -> [ text_header | custom_header ] -> header_property
198(** [text ?all header] creates a text header property request. *)
199
200val addresses : ?all:bool -> [ address_header | custom_header ] -> header_property
201(** [addresses ?all header] creates an addresses header property request. *)
202
203val grouped_addresses : ?all:bool -> [ address_header | custom_header ] -> header_property
204(** [grouped_addresses ?all header] creates a grouped addresses header property request. *)
205
206val message_ids : ?all:bool -> [ message_id_header | custom_header ] -> header_property
207(** [message_ids ?all header] creates a message-ids header property request. *)
208
209val date : ?all:bool -> [ date_header | custom_header ] -> header_property
210(** [date ?all header] creates a date header property request. *)
211
212val urls : ?all:bool -> [ url_header | custom_header ] -> header_property
213(** [urls ?all header] creates a URLs header property request. *)
214
215(** {1 Header Values in Responses}
216
217 When fetching dynamic headers, the response value type depends on the
218 requested form. This type captures all possible response shapes. *)
219
220(** A header value from the response.
221
222 The variant encodes both the form and whether [:all] was requested:
223 - [*_single] variants: value of the last header instance, or [None] if absent
224 - [*_all] variants: list of values for all instances, empty if absent *)
225type header_value =
226 | String_single of string option
227 (** Raw or Text form, single instance. *)
228
229 | String_all of string list
230 (** Raw or Text form, all instances. *)
231
232 | Addresses_single of Mail_address.t list option
233 (** Addresses form, single instance. *)
234
235 | Addresses_all of Mail_address.t list list
236 (** Addresses form, all instances. *)
237
238 | Grouped_single of Mail_address.Group.t list option
239 (** GroupedAddresses form, single instance. *)
240
241 | Grouped_all of Mail_address.Group.t list list
242 (** GroupedAddresses form, all instances. *)
243
244 | Date_single of Ptime.t option
245 (** Date form, single instance. *)
246
247 | Date_all of Ptime.t option list
248 (** Date form, all instances. *)
249
250 | Strings_single of string list option
251 (** MessageIds or URLs form, single instance. *)
252
253 | Strings_all of string list option list
254 (** MessageIds or URLs form, all instances. *)
255
256val header_value_jsont : form:form -> all:bool -> header_value Jsont.t
257(** [header_value_jsont ~form ~all] returns a JSON codec for header values
258 with the given form and multiplicity. *)
259
260(** {1 Low-level JSON Codecs}
261
262 These codecs are used internally and for custom header processing. *)
263
264(** The raw form - header value as-is. *)
265val raw_jsont : string Jsont.t
266
267(** The text form - decoded and unfolded value. *)
268val text_jsont : string Jsont.t
269
270(** The addresses form - list of email addresses. *)
271val addresses_jsont : Mail_address.t list Jsont.t
272
273(** The grouped addresses form - addresses with group info. *)
274val grouped_addresses_jsont : Mail_address.Group.t list Jsont.t
275
276(** The message IDs form - list of message-id strings. *)
277val message_ids_jsont : string list Jsont.t
278
279(** The date form - parsed RFC 3339 date. *)
280val date_jsont : Ptime.t Jsont.t
281
282(** The URLs form - list of URL strings. *)
283val urls_jsont : string list Jsont.t