My personal data management layer
at main 129 lines 4.1 kB view raw
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6(** Video entry type for Bushel *) 7 8type t = { 9 slug : string; 10 title : string; 11 published_date : Ptime.t; 12 uuid : string; 13 description : string; 14 url : string; 15 talk : bool; 16 paper : string option; 17 project : string option; 18 tags : string list; 19} 20 21type ts = t list 22 23(** {1 Accessors} *) 24 25let slug { slug; _ } = slug 26let title { title; _ } = title 27let uuid { uuid; _ } = uuid 28let url { url; _ } = url 29let description { description; _ } = description 30let body = description (* Alias for consistency *) 31let talk { talk; _ } = talk 32let paper { paper; _ } = paper 33let project { project; _ } = project 34let tags { tags; _ } = tags 35 36let date { published_date; _ } = Ptime.to_date published_date 37let datetime { published_date; _ } = published_date 38 39(** {1 Comparison} *) 40 41let compare a b = Ptime.compare b.published_date a.published_date 42 43(** {1 Lookup} *) 44 45let lookup videos uuid = List.find_opt (fun v -> v.uuid = uuid) videos 46let lookup_by_slug videos slug = List.find_opt (fun v -> v.slug = slug) videos 47 48(** {1 Jsont Codec} *) 49 50let jsont : t Jsont.t = 51 let open Jsont in 52 let open Jsont.Object in 53 let make title published_date uuid url talk tags paper project = 54 { slug = uuid; title; published_date; uuid; description = ""; url; 55 talk; paper; project; tags } 56 in 57 map ~kind:"Video" make 58 |> mem "title" string ~enc:(fun v -> v.title) 59 |> mem "published_date" Bushel_types.ptime_jsont ~enc:(fun v -> v.published_date) 60 |> mem "uuid" string ~enc:(fun v -> v.uuid) 61 |> mem "url" string ~enc:(fun v -> v.url) 62 |> mem "talk" bool ~dec_absent:false ~enc:(fun v -> v.talk) 63 |> mem "tags" (list string) ~dec_absent:[] ~enc:(fun v -> v.tags) 64 |> mem "paper" Bushel_types.string_option_jsont ~dec_absent:None 65 ~enc_omit:Option.is_none ~enc:(fun v -> v.paper) 66 |> mem "project" Bushel_types.string_option_jsont ~dec_absent:None 67 ~enc_omit:Option.is_none ~enc:(fun v -> v.project) 68 |> finish 69 70(** {1 Parsing} *) 71 72let of_frontmatter (fm : Frontmatter.t) : (t, string) result = 73 match Frontmatter.decode jsont fm with 74 | Error e -> Error e 75 | Ok v -> 76 Ok { v with 77 slug = v.uuid; 78 description = Frontmatter.body fm } 79 80(** {1 YAML Serialization} *) 81 82let to_yaml t = 83 let open Yamlrw.Util in 84 let fields = [ 85 ("title", string t.title); 86 ("description", string t.description); 87 ("url", string t.url); 88 ("uuid", string t.uuid); 89 ("slug", string t.slug); 90 ("published_date", string (Ptime.to_rfc3339 t.published_date)); 91 ("talk", bool t.talk); 92 ("tags", strings t.tags); 93 ] in 94 let fields = match t.paper with 95 | None -> fields 96 | Some p -> ("paper", string p) :: fields 97 in 98 let fields = match t.project with 99 | None -> fields 100 | Some p -> ("project", string p) :: fields 101 in 102 obj fields 103 104(** {1 Pretty Printing} *) 105 106let pp ppf v = 107 let open Fmt in 108 pf ppf "@[<v>"; 109 pf ppf "%a: %a@," (styled `Bold string) "Type" (styled `Cyan string) "Video"; 110 pf ppf "%a: %a@," (styled `Bold string) "Slug" string (slug v); 111 pf ppf "%a: %a@," (styled `Bold string) "UUID" string (uuid v); 112 pf ppf "%a: %a@," (styled `Bold string) "Title" string (title v); 113 let (year, month, day) = date v in 114 pf ppf "%a: %04d-%02d-%02d@," (styled `Bold string) "Date" year month day; 115 pf ppf "%a: %a@," (styled `Bold string) "URL" string (url v); 116 pf ppf "%a: %b@," (styled `Bold string) "Talk" (talk v); 117 (match paper v with 118 | Some p -> pf ppf "%a: %a@," (styled `Bold string) "Paper" string p 119 | None -> ()); 120 (match project v with 121 | Some p -> pf ppf "%a: %a@," (styled `Bold string) "Project" string p 122 | None -> ()); 123 let t = tags v in 124 if t <> [] then 125 pf ppf "%a: @[<h>%a@]@," (styled `Bold string) "Tags" (list ~sep:comma string) t; 126 pf ppf "@,"; 127 pf ppf "%a:@," (styled `Bold string) "Description"; 128 pf ppf "%a@," string v.description; 129 pf ppf "@]"