123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130(*******************************************************************************)(* Volgo - a Versatile OCaml Library for Git Operations *)(* Copyright (C) 2024-2025 Mathieu Barbin <mathieu.barbin@gmail.com> *)(* *)(* This file is part of Volgo. *)(* *)(* Volgo is free software; you can redistribute it and/or modify it under *)(* the terms of the GNU Lesser General Public License as published by the *)(* Free Software Foundation either version 3 of the License, or any later *)(* version, with the LGPL-3.0 Linking Exception. *)(* *)(* Volgo is distributed in the hope that it will be useful, but WITHOUT ANY *)(* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS *)(* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License and *)(* the file `NOTICE.md` at the root of this repository for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* and the LGPL-3.0 Linking Exception along with this library. If not, see *)(* <http://www.gnu.org/licenses/> and <https://spdx.org>, respectively. *)(*******************************************************************************)moduleList=structincludeListLabelsletmapt~f=map~ftendincludeDynletinline_recordconsfields=Dyn.variantcons[Dyn.recordfields]letto_sexp=letmoduleSexp=Sexplib0.SexpinletmoduleS=Sexplib0.Sexp_convinletrecaux(dyn:Dyn.t):Sexp.t=match[@coverageoff]dynwith|Opaque->Atom"<opaque>"|Unit->List[]|Inti->S.sexp_of_inti|Int32i->S.sexp_of_int32i|Recordfields->List(List.mapfields~f:(fun(field,t)->Sexp.List[Atomfield;auxt]))|Variant(v,args)->(* Special pretty print of variants holding records. *)(matchargswith|[]->Atomv|[Recordfields]->List(Atomv::List.mapfields~f:(fun(field,t)->Sexp.List[Atomfield;auxt]))|_->List(Atomv::List.mapargs~f:aux))|Boolb->S.sexp_of_boolb|Stringa->S.sexp_of_stringa|Bytesa->S.sexp_of_bytesa|Int64i->S.sexp_of_int64i|Nativeinti->S.sexp_of_nativeinti|Charc->S.sexp_of_charc|Floatf->S.sexp_of_floatf|Optiono->S.sexp_of_optionauxo|Listl->S.sexp_of_listauxl|Arraya->S.sexp_of_arrayauxa|Tuplet->List(List.mapt~f:aux)|Mapm->List(List.mapm~f:(fun(k,v)->Sexp.List[auxk;auxv]))|Sets->List(List.maps~f:aux)inaux;;typejson=[`Null|`Boolofbool|`Intofint|`Floatoffloat|`Stringofstring|`Assocof(string*json)list|`Listofjsonlist]letto_json=(* JavaScript's Number.MAX_SAFE_INTEGER = 2^53 - 1. We use Int64 literals to
avoid overflow on 32-bit architectures. To verify these values, run:
[node -p "Number.MAX_SAFE_INTEGER"] and [node -p "Number.MIN_SAFE_INTEGER"]. *)letmax_safe_int64=9007199254740991Linletmin_safe_int64=-9007199254740991Linletrecaux(dyn:Dyn.t):json=match[@coverageoff]dynwith|Opaque->`String"<opaque>"|Unit->`Null|Inti->leti64=Int64.of_intiinifi64>=min_safe_int64&&i64<=max_safe_int64then`Intielse`String(Int.to_stringi)|Int32i->(* On 32-bit architectures, Int32 (32 bits) may not fit in int (31 bits). *)leti'=Int32.to_intiinifInt32.equal(Int32.of_inti')ithen`Inti'else`String(Int32.to_stringi)|Int64i->`String(Int64.to_stringi)|Nativeinti->`String(Nativeint.to_stringi)|Boolb->`Boolb|Strings->`Strings|Bytesb->`String(Bytes.to_stringb)|Charc->`String(String.make1c)|Floatf->`Floatf|OptionNone->`Null|Option(Somet)->auxt|Listl->`List(List.mapl~f:aux)|Arraya->`List(Array.to_lista|>List.map~f:aux)|Tuplet->`List(List.mapt~f:aux)|Recordfields->`Assoc(List.mapfields~f:(fun(k,v)->k,auxv))|Variant(v,[])->`Stringv|Variant(v,[Recordfields])->(* Special treatment for inline records: include variant name as a field. *)`Assoc(("type",`Stringv)::List.mapfields~f:(fun(k,v)->k,auxv))|Variant(v,args)->`Assoc[v,`List(List.mapargs~f:aux)]|Mapm->(* If all keys are strings, serialize as a JSON object. *)letstring_keys=List.filter_mapm~f:(fun(k,v)->matchkwith|Strings->Some(s,v)|_->None)inifList.lengthstring_keys=List.lengthmthen`Assoc(List.mapstring_keys~f:(fun(k,v)->k,auxv))else`List(List.mapm~f:(fun(k,v)->`List[auxk;auxv]))|Sets->`List(List.maps~f:aux)inaux;;