OCaml library for JSONfeed parsing and creation
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2024 Anil Madhavapeddy. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6module Unknown = struct 7 type t = Jsont.json 8 9 let empty = Jsont.Object ([], Jsont.Meta.none) 10 let is_empty = function Jsont.Object ([], _) -> true | _ -> false 11end 12 13type content = [ `Html of string | `Text of string | `Both of string * string ] 14 15type t = { 16 id : string; 17 content : content; 18 url : string option; 19 external_url : string option; 20 title : string option; 21 summary : string option; 22 image : string option; 23 banner_image : string option; 24 date_published : Ptime.t option; 25 date_modified : Ptime.t option; 26 authors : Author.t list option; 27 tags : string list option; 28 language : string option; 29 attachments : Attachment.t list option; 30 references : Reference.t list option; 31 unknown : Unknown.t; 32} 33 34let create ~id ~content ?url ?external_url ?title ?summary ?image ?banner_image 35 ?date_published ?date_modified ?authors ?tags ?language ?attachments 36 ?references ?(unknown = Unknown.empty) () = 37 { 38 id; 39 content; 40 url; 41 external_url; 42 title; 43 summary; 44 image; 45 banner_image; 46 date_published; 47 date_modified; 48 authors; 49 tags; 50 language; 51 attachments; 52 references; 53 unknown; 54 } 55 56let id t = t.id 57let content t = t.content 58let url t = t.url 59let external_url t = t.external_url 60let title t = t.title 61let summary t = t.summary 62let image t = t.image 63let banner_image t = t.banner_image 64let date_published t = t.date_published 65let date_modified t = t.date_modified 66let authors t = t.authors 67let tags t = t.tags 68let language t = t.language 69let attachments t = t.attachments 70let references t = t.references 71let unknown t = t.unknown 72 73let content_html t = 74 match t.content with 75 | `Html html -> Some html 76 | `Text _ -> None 77 | `Both (html, _) -> Some html 78 79let content_text t = 80 match t.content with 81 | `Html _ -> None 82 | `Text text -> Some text 83 | `Both (_, text) -> Some text 84 85let equal a b = a.id = b.id 86 87let compare a b = 88 Option.compare Ptime.compare a.date_published b.date_published 89 90let pp ppf t = 91 match (t.date_published, t.title) with 92 | Some date, Some title -> 93 let (y, m, d), _ = Ptime.to_date_time date in 94 Format.fprintf ppf "[%04d-%02d-%02d] %s (%s)" y m d title t.id 95 | Some date, None -> 96 let (y, m, d), _ = Ptime.to_date_time date in 97 Format.fprintf ppf "[%04d-%02d-%02d] %s" y m d t.id 98 | None, Some title -> Format.fprintf ppf "%s (%s)" title t.id 99 | None, None -> Format.fprintf ppf "%s" t.id 100 101let pp_summary ppf t = 102 Format.fprintf ppf "%s" (Option.value ~default:t.id t.title) 103 104(* Jsont type *) 105 106let jsont = 107 let kind = "Item" in 108 let doc = "A JSON Feed item" in 109 110 (* Helper to construct item from JSON fields *) 111 let make_from_json id content_html content_text url external_url title summary 112 image banner_image date_published date_modified authors tags language 113 attachments references _extensions unknown = 114 (* Determine content from content_html and content_text *) 115 let content = 116 match (content_html, content_text) with 117 | Some html, Some text -> `Both (html, text) 118 | Some html, None -> `Html html 119 | None, Some text -> `Text text 120 | None, None -> 121 Jsont.Error.msg Jsont.Meta.none 122 "Item must have at least one of content_html or content_text" 123 in 124 { 125 id; 126 content; 127 url; 128 external_url; 129 title; 130 summary; 131 image; 132 banner_image; 133 date_published; 134 date_modified; 135 authors; 136 tags; 137 language; 138 attachments; 139 references; 140 unknown; 141 } 142 in 143 144 Jsont.Object.map ~kind ~doc make_from_json 145 |> Jsont.Object.mem "id" Jsont.string ~enc:id 146 |> Jsont.Object.opt_mem "content_html" Jsont.string ~enc:content_html 147 |> Jsont.Object.opt_mem "content_text" Jsont.string ~enc:content_text 148 |> Jsont.Object.opt_mem "url" Jsont.string ~enc:url 149 |> Jsont.Object.opt_mem "external_url" Jsont.string ~enc:external_url 150 |> Jsont.Object.opt_mem "title" Jsont.string ~enc:title 151 |> Jsont.Object.opt_mem "summary" Jsont.string ~enc:summary 152 |> Jsont.Object.opt_mem "image" Jsont.string ~enc:image 153 |> Jsont.Object.opt_mem "banner_image" Jsont.string ~enc:banner_image 154 |> Jsont.Object.opt_mem "date_published" Rfc3339.jsont ~enc:date_published 155 |> Jsont.Object.opt_mem "date_modified" Rfc3339.jsont ~enc:date_modified 156 |> Jsont.Object.opt_mem "authors" (Jsont.list Author.jsont) ~enc:authors 157 |> Jsont.Object.opt_mem "tags" (Jsont.list Jsont.string) ~enc:tags 158 |> Jsont.Object.opt_mem "language" Jsont.string ~enc:language 159 |> Jsont.Object.opt_mem "attachments" 160 (Jsont.list Attachment.jsont) 161 ~enc:attachments 162 |> Jsont.Object.opt_mem "_references" 163 (Jsont.list Reference.jsont) 164 ~enc:references 165 |> Jsont.Object.opt_mem "_extensions" Jsont.json_object ~enc:(fun _t -> None) 166 |> Jsont.Object.keep_unknown Jsont.json_mems ~enc:unknown 167 |> Jsont.Object.finish