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