(*--------------------------------------------------------------------------- Copyright (c) 2024 Anil Madhavapeddy. All rights reserved. SPDX-License-Identifier: ISC ---------------------------------------------------------------------------*) module Unknown = struct type t = (string * Jsont.json) list let empty = [] let is_empty = function [] -> true | _ -> false end type t = { url : string; mime_type : string; title : string option; size_in_bytes : int64 option; duration_in_seconds : int option; unknown : Unknown.t; } let create ~url ~mime_type ?title ?size_in_bytes ?duration_in_seconds ?(unknown = Unknown.empty) () = { url; mime_type; title; size_in_bytes; duration_in_seconds; unknown } let url t = t.url let mime_type t = t.mime_type let title t = t.title let size_in_bytes t = t.size_in_bytes let duration_in_seconds t = t.duration_in_seconds let unknown t = t.unknown let equal a b = a.url = b.url && a.mime_type = b.mime_type && a.title = b.title && a.size_in_bytes = b.size_in_bytes && a.duration_in_seconds = b.duration_in_seconds let pp ppf t = (* Extract filename from URL *) let filename = try let parts = String.split_on_char '/' t.url in List.nth parts (List.length parts - 1) with _ -> t.url in Format.fprintf ppf "%s (%s" filename t.mime_type; (match t.size_in_bytes with | Some size -> let mb = Int64.to_float size /. (1024. *. 1024.) in Format.fprintf ppf ", %.1f MB" mb | None -> ()); (match t.duration_in_seconds with | Some duration -> let mins = duration / 60 in let secs = duration mod 60 in Format.fprintf ppf ", %dm%ds" mins secs | None -> ()); Format.fprintf ppf ")" let jsont = let kind = "Attachment" in let doc = "An attachment object" in let unknown_mems : (Unknown.t, Jsont.json, Jsont.mem list) Jsont.Object.Mems.map = let open Jsont.Object.Mems in let dec_empty () = [] in let dec_add _meta (name : string) value acc = ((name, Jsont.Meta.none), value) :: acc in let dec_finish _meta mems = List.rev_map (fun ((name, _meta), value) -> (name, value)) mems in let enc = { enc = (fun (type acc) (f : Jsont.Meta.t -> string -> Jsont.json -> acc -> acc) unknown (acc : acc) -> List.fold_left (fun acc (name, value) -> f Jsont.Meta.none name value acc) acc unknown); } in map ~kind:"Unknown members" Jsont.json ~dec_empty ~dec_add ~dec_finish ~enc in let create_obj url mime_type title size_in_bytes duration_in_seconds unknown = create ~url ~mime_type ?title ?size_in_bytes ?duration_in_seconds ~unknown () in Jsont.Object.map ~kind ~doc create_obj |> Jsont.Object.mem "url" Jsont.string ~enc:url |> Jsont.Object.mem "mime_type" Jsont.string ~enc:mime_type |> Jsont.Object.opt_mem "title" Jsont.string ~enc:title |> Jsont.Object.opt_mem "size_in_bytes" Jsont.int64 ~enc:size_in_bytes |> Jsont.Object.opt_mem "duration_in_seconds" Jsont.int ~enc:duration_in_seconds |> Jsont.Object.keep_unknown unknown_mems ~enc:unknown |> Jsont.Object.finish