OCaml library for JSONfeed parsing and creation
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(** JSON Feed format parser and serializer.
7
8 @see <https://www.jsonfeed.org/version/1.1/> JSON Feed Specification *)
9
10type t
11(** The type representing a complete JSON Feed. *)
12
13val jsont : t Jsont.t
14(** Declarative type that describes the structure of JSON Feeds.
15
16 Maps the complete JSON Feed 1.1 specification including all required and
17 optional fields. *)
18
19module Unknown : sig
20 type t = Jsont.json
21 (** Unknown or unrecognized JSON object members as a generic JSON object.
22 Useful for preserving fields from custom extensions or future spec
23 versions. *)
24
25 val empty : t
26 (** [empty] is the empty list of unknown fields. *)
27
28 val is_empty : t -> bool
29 (** [is_empty u] returns [true] if there are no unknown fields. *)
30end
31
32(** {1 Construction} *)
33
34val create :
35 title:string ->
36 ?home_page_url:string ->
37 ?feed_url:string ->
38 ?description:string ->
39 ?user_comment:string ->
40 ?next_url:string ->
41 ?icon:string ->
42 ?favicon:string ->
43 ?authors:Author.t list ->
44 ?language:string ->
45 ?expired:bool ->
46 ?hubs:Hub.t list ->
47 items:Item.t list ->
48 ?unknown:Unknown.t ->
49 unit ->
50 t
51(** [create ~title ~items ()] creates a new JSON Feed.
52
53 @param title
54 The name of the feed. Required field that should be plain text, not HTML.
55 @param home_page_url
56 The URL of the resource that the feed describes. This resource may or may
57 not actually be a "home" page, but it should be an HTML page. If a feed is
58 for a podcast, for instance, the home_page_url would be the URL for the
59 podcast's website.
60 @param feed_url
61 The URL of the feed itself. This is the URL that was requested to get this
62 JSON Feed response. Helps feed readers to determine when they're being
63 redirected. Strongly recommended for feeds.
64 @param description
65 A plain text description of the feed, for human consumption. May contain
66 some formatting (like newlines).
67 @param user_comment
68 A description of the purpose of the feed, for a person looking at the raw
69 JSON. This is for the publisher's use only, not intended to be displayed
70 to the user.
71 @param next_url
72 The URL of a feed that provides the next n items, where n is determined by
73 the publisher. Used for pagination. A feed reader may continue to request
74 the URLs in next_url until it reaches a feed without a next_url.
75 @param icon
76 The URL of an image for the feed suitable to be used in a timeline, much
77 the way an avatar might be used. Should be square and relatively large -
78 such as 512 x 512 pixels - and may be cropped to a circle or rounded
79 corners. Should not be transparent.
80 @param favicon
81 The URL of an image for the feed suitable to be used in a source list.
82 Should be square and relatively small - such as 64 x 64 pixels. Should not
83 be transparent.
84 @param authors
85 Specifies one or more feed authors. The author object has several members
86 (name, url, avatar) which are all optional, but at least one must be
87 present for the object to be valid.
88 @param language
89 The primary language for the feed in RFC 5646 format. The value can be a
90 language tag such as "en" or "en-US", or a language-region combination.
91 @param expired
92 Whether or not the feed is finished - that is, whether or not it will ever
93 update again. A feed for a temporary event, like an instance of a
94 conference, may expire. If the value is [true], feed readers may stop
95 checking for updates.
96 @param hubs
97 Endpoints that can be used to subscribe to real-time notifications from
98 the publisher of this feed. Each hub object has a type (such as "rssCloud"
99 or "WebSub") and url.
100 @param items
101 The items in the feed. Required field, though it may be an empty array.
102 @param unknown
103 Unknown JSON object members preserved from parsing. Useful for custom
104 extensions. *)
105
106(** {1 Accessors} *)
107
108val version : t -> string
109(** [version feed] returns the URL of the version of the format the feed uses.
110 This will always be "https://jsonfeed.org/version/1.1" for feeds created
111 with this library. This is a required field in the JSON Feed spec. *)
112
113val title : t -> string
114(** [title feed] returns the name of the feed. This is plain text and should not
115 contain HTML. This is a required field. *)
116
117val home_page_url : t -> string option
118(** [home_page_url feed] returns the URL of the resource that the feed
119 describes. This resource may or may not actually be a "home" page, but it
120 should be an HTML page. For instance, if a feed is for a podcast, the
121 home_page_url would be the URL for the podcast's website. *)
122
123val feed_url : t -> string option
124(** [feed_url feed] returns the URL of the feed itself. This should be the URL
125 that was requested to get this JSON Feed response. It helps feed readers
126 determine when they're being redirected. This is strongly recommended for
127 feeds. *)
128
129val description : t -> string option
130(** [description feed] returns a plain text description of the feed, for human
131 consumption. This field may contain some formatting such as newlines. *)
132
133val user_comment : t -> string option
134(** [user_comment feed] returns a description of the purpose of the feed, for a
135 person looking at the raw JSON. This is for the publisher's use only and is
136 not intended to be displayed to end users. *)
137
138val next_url : t -> string option
139(** [next_url feed] returns the URL of a feed that provides the next n items,
140 where n is determined by the publisher. This is used for pagination. A feed
141 reader may continue to request the URLs in next_url until it reaches a feed
142 without a next_url. *)
143
144val icon : t -> string option
145(** [icon feed] returns the URL of an image for the feed suitable to be used in
146 a timeline, much the way an avatar might be used. It should be square and
147 relatively large (such as 512 x 512 pixels) and may be cropped to a circle
148 or rounded corners by feed readers. It should not be transparent. *)
149
150val favicon : t -> string option
151(** [favicon feed] returns the URL of an image for the feed suitable to be used
152 in a source list. It should be square and relatively small (such as 64 x 64
153 pixels) and should not be transparent. *)
154
155val authors : t -> Author.t list option
156(** [authors feed] returns the feed authors. Each author object has several
157 members (name, url, avatar) which are all optional, but at least one must be
158 present for the object to be valid. If a feed has multiple authors, they
159 should all be listed here. *)
160
161val language : t -> string option
162(** [language feed] returns the primary language for the feed in RFC 5646
163 format. The value can be a language tag such as "en" or "en-US", or a
164 language-region combination. This field helps feed readers present the feed
165 in the appropriate language. *)
166
167val expired : t -> bool option
168(** [expired feed] returns whether the feed is finished - that is, whether it
169 will ever update again. A feed for a temporary event, like an instance of a
170 conference, may expire. If the value is [Some true], feed readers may stop
171 checking for updates. *)
172
173val hubs : t -> Hub.t list option
174(** [hubs feed] returns endpoints that can be used to subscribe to real-time
175 notifications from the publisher of this feed. Each hub object has a type
176 (such as "rssCloud" or "WebSub") and a url. Feed readers can use these to
177 get immediate updates when new items are published. *)
178
179val items : t -> Item.t list
180(** [items feed] returns the array of items in the feed. This is a required
181 field, though it may be an empty list. Items represent the individual
182 entries in the feed - blog posts, podcast episodes, microblog posts, etc. *)
183
184val unknown : t -> Unknown.t
185(** [unknown feed] returns any unknown JSON object members that were preserved
186 during parsing. This is useful for custom extensions or fields from future
187 versions of the spec. *)
188
189(** {1 Encoding and Decoding} *)
190
191val decode :
192 ?layout:bool ->
193 ?locs:bool ->
194 ?file:string ->
195 Bytesrw.Bytes.Reader.t ->
196 (t, Jsont.Error.t) result
197(** [decode r] decodes a JSON Feed from bytesrw reader [r].
198
199 @param layout Preserve whitespace for round-tripping (default: false)
200 @param locs Track locations for better error messages (default: false)
201 @param file Source file name for error reporting *)
202
203val decode_string :
204 ?layout:bool ->
205 ?locs:bool ->
206 ?file:string ->
207 string ->
208 (t, Jsont.Error.t) result
209(** [decode_string s] decodes a JSON Feed from string [s]. *)
210
211val encode :
212 ?format:Jsont.format ->
213 ?number_format:Jsont.number_format ->
214 t ->
215 eod:bool ->
216 Bytesrw.Bytes.Writer.t ->
217 (unit, Jsont.Error.t) result
218(** [encode feed w] encodes [feed] to bytesrw writer [w].
219
220 @param format
221 Output formatting: [Jsont.Minify] or [Jsont.Indent] (default: Minify)
222 @param number_format Printf format for numbers (default: "%.16g")
223 @param eod Write end-of-data marker *)
224
225val encode_string :
226 ?format:Jsont.format ->
227 ?number_format:Jsont.number_format ->
228 t ->
229 (string, Jsont.Error.t) result
230(** [encode_string feed] encodes [feed] to a string. *)
231
232val of_string : string -> (t, Jsont.Error.t) result
233(** Alias for [decode_string] with default options. *)
234
235val to_string : ?minify:bool -> t -> (string, Jsont.Error.t) result
236(** [to_string feed] encodes [feed] to string.
237 @param minify Use compact format (true) or indented (false, default) *)
238
239(** {1 Validation} *)
240
241val validate : t -> (unit, string list) result
242(** [validate feed] validates the feed structure. Checks for unique item IDs,
243 valid content, etc. *)
244
245(** {1 Comparison} *)
246
247val equal : t -> t -> bool
248(** [equal a b] tests equality between two feeds. *)
249
250(** {1 Pretty Printing} *)
251
252val pp : Format.formatter -> t -> unit
253val pp_summary : Format.formatter -> t -> unit
254
255(** {1 Submodules} *)
256
257module Rfc3339 = Rfc3339
258module Cito = Cito
259module Author = Author
260module Attachment = Attachment
261module Hub = Hub
262module Reference = Reference
263module Item = Item