123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938(* JSON structure description using dependently typed combinators. *)(************************************************************************)(* json-data-encoding *)(* *)(* Copyright 2014 OCamlPro *)(* *)(* This file is distributed under the terms of the GNU Lesser General *)(* Public License as published by the Free Software Foundation; either *)(* version 2.1 of the License, or (at your option) any later version, *)(* with the OCaml static compilation exception. *)(* *)(* It 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 General Public License for more details. *)(* *)(************************************************************************)exceptionUnexpectedofstring*stringexceptionNo_case_matchedofexnlistexceptionBad_array_sizeofint*intexceptionMissing_fieldofstringexceptionUnexpected_fieldofstringexceptionBad_schemaofexnexceptionCannot_destructof(Json_query.path*exn)(*-- types and errors --------------------------------------------------------*)letunexpectedkindexpected=letkind=matchkindwith|`O[]->"empty object"|`A[]->"empty array"|`O_->"object"|`A_->"array"|`Null->"null"|`String_->"string"|`Float_->"number"|`Bool_->"boolean"inCannot_destruct([],Unexpected(kind,expected))type'trepr_agnostic_custom={write:'rt.(moduleJson_repr.Reprwithtypevalue='rt)->'t->'rt;read:'rf.(moduleJson_repr.Reprwithtypevalue='rf)->'rf->'t}(* The GADT definition for encodings. This type must be kept internal
because it does not encode all invariants. Some properties are
checked at encoding construction time by smart constructors, since
checking them would either be impossible, or would make the type
too complex. In a few corners that involve custom encodings using
user defined functions, some properties cannot be checked until
construction/destruction time. If such a run time check fails, is
denotes a programmer error and an [Invalid_argument] exceptions is
thus raised. *)type_encoding=|Null:unitencoding|Empty:unitencoding|Ignore:unitencoding|Option:'aencoding->'aoptionencoding|Constant:string->unitencoding|Int:'aint_encoding->'aencoding|Bool:boolencoding|String:stringencoding|Float:boundsoption->floatencoding|Array:'aencoding->'aarrayencoding|Obj:'afield->'aencoding|Objs:'aencoding*'bencoding->('a*'b)encoding|Tup:'aencoding->'aencoding|Tups:'aencoding*'bencoding->('a*'b)encoding|Custom:'trepr_agnostic_custom*Json_schema.schema->'tencoding|Conv:('a->'b)*('b->'a)*'bencoding*Json_schema.schemaoption->'aencoding|Describe:{id:string;title:stringoption;description:stringoption;encoding:'aencoding}->'aencoding|Mu:{id:string;title:stringoption;description:stringoption;self:('aencoding->'aencoding);}->'aencoding|Union:'tcaselist->'tencodingand'aint_encoding={int_name:string;of_float:float->'a;to_float:'a->float;lower_bound:'a;upper_bound:'a}andbounds={float_name:string;minimum:float;maximum:float}and_field=|Req:{name:string;encoding:'aencoding;title:stringoption;description:stringoption;}->'afield|Opt:{name:string;encoding:'aencoding;title:stringoption;description:stringoption;}->'aoptionfield|Dft:{name:string;encoding:'aencoding;title:stringoption;description:stringoption;default:'a;}->'afieldand'tcase=|Case:{encoding:'aencoding;title:stringoption;description:stringoption;proj:('t->'aoption);inj:('a->'t)}->'tcase(*-- construct / destruct / schema over the main GADT forms ------------------*)moduletypeS=sigtyperepr_valuevalconstruct:'tencoding->'t->repr_valuevaldestruct:'tencoding->repr_value->'tvalcustom:('t->repr_value)->(repr_value->'t)->schema:Json_schema.schema->'tencodingendmoduleMake(Repr:Json_repr.Repr):Swithtyperepr_value=Repr.value=structtyperepr_value=Repr.valueletconstructencv=letrecconstruct:typet.tencoding->t->Repr.value=function|Null->(fun()->Repr.repr`Null)|Empty->(fun()->Repr.repr(`O[]))|Ignore->(fun()->Repr.repr(`O[]))|Optiont->(function|None->Repr.repr`Null|Somev->constructtv)|Constantstr->(fun()->Repr.repr(`Stringstr))|Int{int_name;to_float;lower_bound;upper_bound}->(fun(i:t)->ifi<lower_bound||i>upper_boundtheninvalid_arg("Json_encoding.construct: "^int_name^" out of range");Repr.repr(`Float(to_floati)))|Bool->(fun(b:t)->Repr.repr(`Boolb))|String->(funs->Repr.repr(`Strings))|Float(Some{minimum;maximum;float_name})->leterr="Json_encoding.construct: "^float_name^" out of range"in(funfloat->iffloat<minimum||float>maximumtheninvalid_argerr;Repr.repr(`Floatfloat))|FloatNone->(funfloat->Repr.repr(`Floatfloat))|Describe{encoding=t}->constructt|Custom({write},_)->(fun(j:t)->write(moduleRepr)j)|Conv(ffrom,_,t,_)->(funv->constructt(ffromv))|Mu{self}asenc->construct(selfenc)|Arrayt->letwv=constructtvin(funarr->Repr.repr(`A(Array.to_list(Array.mapwarr))))|Obj(Req{name=n;encoding=t})->letwv=constructtvin(funv->Repr.repr(`O[n,wv]))|Obj(Dft{name=n;encoding=t;default=d})->letwv=constructtvin(funv->Repr.repr(`O(ifv<>dthen[n,wv]else[])))|Obj(Opt{name=n;encoding=t})->letwv=constructtvin(functionNone->Repr.repr(`O[])|Somev->Repr.repr(`O[n,wv]))|Objs(o1,o2)->letw1v=constructo1vinletw2v=constructo2vin(function(v1,v2)->matchRepr.view(w1v1),Repr.view(w2v2)with|`Ol1,`Ol2->Repr.repr(`O(l1@l2))|`Null,`Null|_->invalid_arg"Json_encoding.construct: consequence of bad merge_objs")|Tupt->letwv=constructtvin(funv->Repr.repr(`A[wv]))|Tups(o1,o2)->letw1v=constructo1vinletw2v=constructo2vin(function(v1,v2)->matchRepr.view(w1v1),Repr.view(w2v2)with|`Al1,`Al2->Repr.repr(`A(l1@l2))|_->invalid_arg"Json_encoding.construct: consequence of bad merge_tups")|Unioncases->(funv->letrecdo_cases=function|[]->invalid_arg"Json_encoding.construct: consequence of bad union"|Case{encoding;proj}::rest->matchprojvwith|Somev->constructencodingv|None->do_casesrestindo_casescases)inconstructencvletrecdestruct:typet.tencoding->(Repr.value->t)=function|Null->(funv->matchRepr.viewvwith`Null->()|k->raise(unexpectedk"null"))|Empty->(funv->matchRepr.viewvwith|`O[]->()|`O[f,_]->raise(Cannot_destruct([],Unexpected_fieldf))|k->raise@@unexpectedk"an empty object")|Ignore->(funv->matchRepr.viewvwith_->())|Optiont->(funv->matchRepr.viewvwith|`Null->None|_->Some(destructtv))|Constantstr->(funv->matchRepr.viewvwith|`Stringswhens=str->()|x->raise@@unexpectedxstr)|Int{int_name;of_float;to_float;lower_bound;upper_bound}->letlower_bound=to_floatlower_boundinletupper_bound=to_floatupper_boundin(funv->matchRepr.viewvwith|`Floatv->letrest,v=modfvinifrest<>0.thenbeginletexn=Failure(int_name^" cannot have a fractional part")inraise(Cannot_destruct([],exn))end;ifv<lower_bound||v>upper_boundthenbeginletexn=Failure(int_name^" out of range")inraise(Cannot_destruct([],exn))end;of_floatv|k->raise(unexpectedk"number"))|Bool->(funv->matchRepr.viewvwith`Boolb->(b:t)|k->raise(unexpectedk"boolean"))|String->(funv->matchRepr.viewvwith`Strings->s|k->raise(unexpectedk"string"))|FloatNone->(funv->matchRepr.viewvwith`Floatf->f|k->raise(unexpectedk"float"))|Float(Some{minimum;maximum;float_name})->(funv->matchRepr.viewvwith|`Floatf->iff<minimum||f>maximumthenletexn=Failure(float_name^" out of range")inraise(Cannot_destruct([],exn))elsef|k->raise(unexpectedk"float"))|Describe{encoding=t}->destructt|Custom({read},_)->read(moduleRepr)|Conv(_,fto,t,_)->(funv->fto(destructtv))|Mu{self}asenc->destruct(selfenc)|Arrayt->(funv->matchRepr.viewvwith|`O[]->(* Weak `Repr`s like BSON don't know the difference *)[||]|`Acells->Array.mapi(funicell->trydestructtcellwithCannot_destruct(path,err)->raise(Cannot_destruct(`Indexi::path,err)))(Array.of_listcells)|k->raise@@unexpectedk"array")|Obj_ast->letd=destruct_objtin(funv->matchRepr.viewvwith|`Ofields->letr,rest,ign=dfieldsinbeginmatchrestwith|(field,_)::_whennotign->raise@@Unexpected_fieldfield|_->rend|k->raise@@unexpectedk"object")|Objs_ast->letd=destruct_objtin(funv->matchRepr.viewvwith|`Ofields->letr,rest,ign=dfieldsinbeginmatchrestwith|(field,_)::_whennotign->raise@@Unexpected_fieldfield|_->rend|k->raise@@unexpectedk"object")|Tup_ast->letr,i=destruct_tup0tin(funv->matchRepr.viewvwith|`Acells->letcells=Array.of_listcellsinletlen=Array.lengthcellsinifi<>Array.lengthcellsthenraise(Cannot_destruct([],Bad_array_size(len,i)))elsercells|k->raise@@unexpectedk"array")|Tups_ast->letr,i=destruct_tup0tin(funv->matchRepr.viewvwith|`Acells->letcells=Array.of_listcellsinletlen=Array.lengthcellsinifi<>Array.lengthcellsthenraise(Cannot_destruct([],Bad_array_size(len,i)))elsercells|k->raise@@unexpectedk"array")|Unioncases->(funv->letrecdo_caseserrs=function|[]->raise(Cannot_destruct([],No_case_matched(List.reverrs)))|Case{encoding;inj}::rest->tryinj(destructencodingv)witherr->do_cases(err::errs)restindo_cases[]cases)anddestruct_tup:typet.int->tencoding->(Repr.valuearray->t)*int=funit->matchtwith|Tupt->(funarr->(trydestructtarr.(i)withCannot_destruct(path,err)->raise(Cannot_destruct(`Indexi::path,err)))),succi|Tups(t1,t2)->letr1,i=destruct_tupit1inletr2,i=destruct_tupit2in(funarr->r1arr,r2arr),i|Conv(_,fto,t,_)->letr,i=destruct_tupitin(funarr->fto(rarr)),i|Mu{self}asenc->destruct_tupi(selfenc)|Describe{encoding}->destruct_tupiencoding|_->invalid_arg"Json_encoding.destruct: consequence of bad merge_tups"anddestruct_obj:typet.tencoding->(string*Repr.value)list->t*(string*Repr.value)list*bool=funt->letrecassocaccn=function|[]->raiseNot_found|(f,v)::restwhenn=f->v,acc@rest|oth::rest->assoc(oth::acc)nrestinmatchtwith|Empty->(funfields->(),fields,false)|Ignore->(funfields->(),fields,true)|Obj(Req{name=n;encoding=t})->(funfields->tryletv,rest=assoc[]nfieldsindestructtv,rest,falsewith|Not_found->raise(Cannot_destruct([],Missing_fieldn))|Cannot_destruct(path,err)->raise(Cannot_destruct(`Fieldn::path,err)))|Obj(Opt{name=n;encoding=t})->(funfields->tryletv,rest=assoc[]nfieldsinSome(destructtv),rest,falsewith|Not_found->None,fields,false|Cannot_destruct(path,err)->raise(Cannot_destruct(`Fieldn::path,err)))|Obj(Dft{name=n;encoding=t;default=d})->(funfields->tryletv,rest=assoc[]nfieldsindestructtv,rest,falsewith|Not_found->d,fields,false|Cannot_destruct(path,err)->raise(Cannot_destruct(`Fieldn::path,err)))|Objs(o1,o2)->letd1=destruct_objo1inletd2=destruct_objo2in(funfields->letr1,rest,ign1=d1fieldsinletr2,rest,ign2=d2restin(r1,r2),rest,ign1||ign2)|Conv(_,fto,t,_)->letd=destruct_objtin(funfields->letr,rest,ign=dfieldsinftor,rest,ign)|Mu{self}asenc->destruct_obj(selfenc)|Describe{encoding}->destruct_objencoding|Unioncases->(funfields->letrecdo_caseserrs=function|[]->raise(Cannot_destruct([],No_case_matched(List.reverrs)))|Case{encoding;inj}::rest->tryletr,rest,ign=destruct_objencodingfieldsininjr,rest,ignwitherr->do_cases(err::errs)restindo_cases[]cases)|_->invalid_arg"Json_encoding.destruct: consequence of bad merge_objs"letcustomwriteread~schema=letread:typetf.(moduleJson_repr.Reprwithtypevalue=tf)->tf->'t=fun(moduleRepr_f)repr->read(Json_repr.convert(moduleRepr_f)(moduleRepr)repr)inletwrite:typetf.(moduleJson_repr.Reprwithtypevalue=tf)->'t->tf=fun(moduleRepr_f)v->Json_repr.convert(moduleRepr)(moduleRepr_f)(writev)inCustom({read;write},schema)endmoduleEzjsonm_encoding=Make(Json_repr.Ezjsonm)letpatch_description?title?description(elt:Json_schema.element)=matchtitle,descriptionwith|None,None->elt|Some_,None->{eltwithtitle}|None,Some_->{eltwithdescription}|Some_,Some_->{eltwithtitle;description}letschema?definitions_pathencoding=letopenJson_schemainletsch=refanyinletrecprodl1l2=matchl1with|[]->[]|(l1,b1)::es->List.map(fun(l2,b2)->l1@l2,b1||b2)l2@prodesl2inletrecobject_schema:typet.tencoding->((string*element*bool*Json_repr.anyoption)list*bool)list=function|Conv(_,_,o,None)->object_schemao|Empty->[[],false]|Ignore->[[],true]|Obj(Req{name=n;encoding=t;title;description})->[[n,patch_description?title?description(schemat),true,None],false]|Obj(Opt{name=n;encoding=t;title;description})->[[n,patch_description?title?description(schemat),false,None],false]|Obj(Dft{name=n;encoding=t;title;description;default=d})->letd=Json_repr.repr_to_any(moduleJson_repr.Ezjsonm)(Ezjsonm_encoding.constructtd)in[[n,patch_description?title?description(schemat),false,Somed],false]|Objs(o1,o2)->prod(object_schemao1)(object_schemao2)|Union[]->invalid_arg"Json_encoding.schema: empty union in object"|Unioncases->List.flatten(List.map(fun(Case{encoding=o})->object_schemao)cases)|Mu{self}asenc->object_schema(selfenc)|Describe{encoding=t}->object_schemat|Conv(_,_,_,Some_)(* FIXME: We could do better *)|_->invalid_arg"Json_encoding.schema: consequence of bad merge_objs"andarray_schema:typet.tencoding->elementlist=function|Conv(_,_,o,None)->array_schemao|Tupt->[schemat]|Tups(t1,t2)->array_schemat1@array_schemat2|Mu{self}asenc->array_schema(selfenc)|Describe{encoding=t}->array_schemat|Conv(_,_,_,Some_)(* FIXME: We could do better *)|_->invalid_arg"Json_encoding.schema: consequence of bad merge_tups"andschema:typet.tencoding->element=function|Null->elementNull|Empty->element(Object{object_specswithadditional_properties=None})|Ignore->elementAny|Optiont->element(Combine(One_of,[schemat;elementNull]))|Int{to_float;lower_bound;upper_bound}->letminimum=Some(to_floatlower_bound,`Inclusive)inletmaximum=Some(to_floatupper_bound,`Inclusive)inelement(Integer{multiple_of=None;minimum;maximum})|Bool->elementBoolean|Constantstr->{(element(Stringstring_specs))withenum=Some[Json_repr.to_any(`Stringstr)]}|String->element(Stringstring_specs)|Float(Some{minimum;maximum})->element(Number{multiple_of=None;minimum=Some(minimum,`Inclusive);maximum=Some(maximum,`Inclusive)})|FloatNone->element(Numbernumeric_specs)|Describe{id=name;title;description;encoding}->letschema=patch_description?title?description(schemaencoding)inlets,def=add_definition?definitions_pathnameschema!schinsch:=fst(merge_definitions(!sch,s));def|Custom(_,s)->sch:=fst(merge_definitions(!sch,s));roots|Conv(_,_,_,Somes)->sch:=fst(merge_definitions(!sch,s));roots|Conv(_,_,t,None)->schemat|Mu{id=name;title;description;self=f}->letfake_schema=ifdefinition_exists?definitions_pathname!schthenupdate(definition_ref?definitions_pathname)!schelseletsch,elt=add_definition?definitions_pathname(elementDummy)!schinupdateeltschinletfake_self=Custom({write=(fun__->assertfalse);read=(fun_->assertfalse)},fake_schema)inletroot=patch_description?title?description(schema(ffake_self))inletnsch,def=add_definition?definitions_pathnameroot!schinsch:=nsch;def|Arrayt->element(Monomorphic_array(schemat,array_specs))|Objs_aso->beginmatchobject_schemaowith|[properties,ext]->letadditional_properties=ifextthenSome(elementAny)elseNoneinelement(Object{object_specswithproperties;additional_properties})|more->letelements=List.map(fun(properties,ext)->letadditional_properties=ifextthenSome(elementAny)elseNoneinelement(Object{object_specswithproperties;additional_properties}))moreinelement(Combine(One_of,elements))end|Obj_aso->beginmatchobject_schemaowith|[properties,ext]->letadditional_properties=ifextthenSome(elementAny)elseNoneinelement(Object{object_specswithproperties;additional_properties})|more->letelements=List.map(fun(properties,ext)->letadditional_properties=ifextthenSome(elementAny)elseNoneinelement(Object{object_specswithproperties;additional_properties}))moreinelement(Combine(One_of,elements))end|Tup_ast->element(Array(array_schemat,array_specs))|Tups_ast->element(Array(array_schemat,array_specs))|Unioncases->(* FIXME: smarter merge *)letelements=List.map(fun(Case{encoding;title;description})->patch_description?title?description(schemaencoding))casesinelement(Combine(One_of,elements))inletschema=schemaencodinginupdateschema!sch(*-- utility wrappers over the GADT ------------------------------------------*)letreq?title?descriptionnt=Req{name=n;encoding=t;title;description}letopt?title?descriptionnt=Opt{name=n;encoding=t;title;description}letdft?title?descriptionntd=Dft{name=n;encoding=t;title;description;default=d}letmuname?title?descriptionself=Mu{id=name;title;description;self}letnull=Nullletint=Int{int_name="int";of_float=int_of_float;to_float=float_of_int;(* cross-platform consistent OCaml ints *)lower_bound=-(1lsl30);upper_bound=(1lsl30)-1}letranged_int~minimum:lower_bound~maximum:upper_boundname=ifSys.word_size=64&&(lower_bound<-(1lsl30)||upper_bound>(1lsl30)-1)theninvalid_arg"Json_encoding.ranged_int: bounds out of portable int31 range";Int{int_name=name;of_float=int_of_float;to_float=float_of_int;lower_bound;upper_bound}letint53=Int{int_name="int53";of_float=Int64.of_float;to_float=Int64.to_float;lower_bound=Int64.neg(Int64.shift_left1L53);upper_bound=Int64.shift_left1L53}letranged_int53~minimum:lower_bound~maximum:upper_boundname=iflower_bound<Int64.neg(Int64.shift_left1L53)||upper_bound>Int64.shift_left1L53theninvalid_arg"Json_encoding.ranged_int53: bounds out of JSON-representable integers";Int{int_name=name;of_float=Int64.of_float;to_float=Int64.to_float;lower_bound;upper_bound}letint32=Int{int_name="int32";of_float=Int32.of_float;to_float=Int32.to_float;lower_bound=Int32.min_int;upper_bound=Int32.max_int}letranged_int32~minimum:lower_bound~maximum:upper_boundname=Int{int_name=name;of_float=Int32.of_float;to_float=Int32.to_float;lower_bound;upper_bound}letranged_float~minimum~maximumfloat_name=Float(Some{minimum;maximum;float_name})letfloat=FloatNoneletstring=Stringletconvffromfto?schemat=Conv(ffrom,fto,t,schema)letbytes=Conv(Bytes.to_string,Bytes.of_string,string,None)letbool=Boolletarrayt=Arraytletobj1f1=Objf1letobj2f1f2=Objs(Objf1,Objf2)letobj3f1f2f3=conv(fun(a,b,c)->(a,(b,c)))(fun(a,(b,c))->(a,b,c))(Objs(Objf1,Objs(Objf2,Objf3)))letobj4f1f2f3f4=conv(fun(a,b,c,d)->(a,(b,(c,d))))(fun(a,(b,(c,d)))->(a,b,c,d))(Objs(Objf1,Objs(Objf2,Objs(Objf3,Objf4))))letobj5f1f2f3f4f5=conv(fun(a,b,c,d,e)->(a,(b,(c,(d,e)))))(fun(a,(b,(c,(d,e))))->(a,b,c,d,e))(Objs(Objf1,Objs(Objf2,Objs(Objf3,Objs(Objf4,Objf5)))))letobj6f1f2f3f4f5f6=conv(fun(a,b,c,d,e,f)->(a,(b,(c,(d,(e,f))))))(fun(a,(b,(c,(d,(e,f)))))->(a,b,c,d,e,f))(Objs(Objf1,Objs(Objf2,Objs(Objf3,Objs(Objf4,Objs(Objf5,Objf6))))))letobj7f1f2f3f4f5f6f7=conv(fun(a,b,c,d,e,f,g)->(a,(b,(c,(d,(e,(f,g)))))))(fun(a,(b,(c,(d,(e,(f,g))))))->(a,b,c,d,e,f,g))(letrest=Objs(Objf6,Objf7)inObjs(Objf1,Objs(Objf2,Objs(Objf3,Objs(Objf4,Objs(Objf5,rest))))))letobj8f1f2f3f4f5f6f7f8=conv(fun(a,b,c,d,e,f,g,h)->(a,(b,(c,(d,(e,(f,(g,h))))))))(fun(a,(b,(c,(d,(e,(f,(g,h)))))))->(a,b,c,d,e,f,g,h))(letrest=Objs(Objf6,Objs(Objf7,Objf8))inObjs(Objf1,Objs(Objf2,Objs(Objf3,Objs(Objf4,Objs(Objf5,rest))))))letobj9f1f2f3f4f5f6f7f8f9=conv(fun(a,b,c,d,e,f,g,h,i)->(a,(b,(c,(d,(e,(f,(g,(h,i)))))))))(fun(a,(b,(c,(d,(e,(f,(g,(h,i))))))))->(a,b,c,d,e,f,g,h,i))(letrest=Objs(Objf6,Objs(Objf7,Objs(Objf8,Objf9)))inObjs(Objf1,Objs(Objf2,Objs(Objf3,Objs(Objf4,Objs(Objf5,rest))))))letobj10f1f2f3f4f5f6f7f8f9f10=conv(fun(a,b,c,d,e,f,g,h,i,j)->(a,(b,(c,(d,(e,(f,(g,(h,(i,j))))))))))(fun(a,(b,(c,(d,(e,(f,(g,(h,(i,j)))))))))->(a,b,c,d,e,f,g,h,i,j))(letrest=Objs(Objf6,Objs(Objf7,Objs(Objf8,Objs(Objf9,Objf10))))inObjs(Objf1,Objs(Objf2,Objs(Objf3,Objs(Objf4,Objs(Objf5,rest))))))lettup1f1=Tupf1lettup2f1f2=Tups(Tupf1,Tupf2)lettup3f1f2f3=conv(fun(a,b,c)->(a,(b,c)))(fun(a,(b,c))->(a,b,c))(Tups(Tupf1,Tups(Tupf2,Tupf3)))lettup4f1f2f3f4=conv(fun(a,b,c,d)->(a,(b,(c,d))))(fun(a,(b,(c,d)))->(a,b,c,d))(Tups(Tupf1,Tups(Tupf2,Tups(Tupf3,Tupf4))))lettup5f1f2f3f4f5=conv(fun(a,b,c,d,e)->(a,(b,(c,(d,e)))))(fun(a,(b,(c,(d,e))))->(a,b,c,d,e))(Tups(Tupf1,Tups(Tupf2,Tups(Tupf3,Tups(Tupf4,Tupf5)))))lettup6f1f2f3f4f5f6=conv(fun(a,b,c,d,e,f)->(a,(b,(c,(d,(e,f))))))(fun(a,(b,(c,(d,(e,f)))))->(a,b,c,d,e,f))(Tups(Tupf1,Tups(Tupf2,Tups(Tupf3,Tups(Tupf4,Tups(Tupf5,Tupf6))))))lettup7f1f2f3f4f5f6f7=conv(fun(a,b,c,d,e,f,g)->(a,(b,(c,(d,(e,(f,g)))))))(fun(a,(b,(c,(d,(e,(f,g))))))->(a,b,c,d,e,f,g))(letrest=Tups(Tupf6,Tupf7)inTups(Tupf1,Tups(Tupf2,Tups(Tupf3,Tups(Tupf4,Tups(Tupf5,rest))))))lettup8f1f2f3f4f5f6f7f8=conv(fun(a,b,c,d,e,f,g,h)->(a,(b,(c,(d,(e,(f,(g,h))))))))(fun(a,(b,(c,(d,(e,(f,(g,h)))))))->(a,b,c,d,e,f,g,h))(letrest=Tups(Tupf6,Tups(Tupf7,Tupf8))inTups(Tupf1,Tups(Tupf2,Tups(Tupf3,Tups(Tupf4,Tups(Tupf5,rest))))))lettup9f1f2f3f4f5f6f7f8f9=conv(fun(a,b,c,d,e,f,g,h,i)->(a,(b,(c,(d,(e,(f,(g,(h,i)))))))))(fun(a,(b,(c,(d,(e,(f,(g,(h,i))))))))->(a,b,c,d,e,f,g,h,i))(letrest=Tups(Tupf6,Tups(Tupf7,Tups(Tupf8,Tupf9)))inTups(Tupf1,Tups(Tupf2,Tups(Tupf3,Tups(Tupf4,Tups(Tupf5,rest))))))lettup10f1f2f3f4f5f6f7f8f9f10=conv(fun(a,b,c,d,e,f,g,h,i,j)->(a,(b,(c,(d,(e,(f,(g,(h,(i,j))))))))))(fun(a,(b,(c,(d,(e,(f,(g,(h,(i,j)))))))))->(a,b,c,d,e,f,g,h,i,j))(letrest=Tups(Tupf6,Tups(Tupf7,Tups(Tupf8,Tups(Tupf9,Tupf10))))inTups(Tupf1,Tups(Tupf2,Tups(Tupf3,Tups(Tupf4,Tups(Tupf5,rest))))))letrepr_agnostic_custom{write;read}~schema=Custom({write;read},schema)letconstants=Constantsletstring_enumcases=letschema=letspecs=Json_schema.({pattern=None;min_length=0;max_length=None})inletenum=List.map(fun(s,_)->Json_repr.(repr_to_any(moduleEzjsonm))(`Strings))casesinJson_schema.(update{(element(Stringspecs))withenum=Someenum}any)inletlen=List.lengthcasesinletmcases=Hashtbl.createlenandrcases=Hashtbl.createleninletcases_str=String.concat" "(List.map(funx->"'"^fstx^"'")cases)inList.iter(fun(s,c)->ifHashtbl.memmcasesstheninvalid_arg"Json_encoding.string_enum: duplicate case";Hashtbl.addmcasessc;Hashtbl.addrcasescs)cases;conv(funv->tryHashtbl.findrcasesvwithNot_found->invalid_arg(Format.sprintf"Json_encoding.construct: consequence of non exhaustive Json_encoding.string_enum. Strings are: %s"cases_str))(funs->(tryHashtbl.findmcasesswithNot_found->letrecorpatppf=function|[]->assertfalse|[last,_]->Format.fprintfppf"%S"last|[prev,_;last,_]->Format.fprintfppf"%S or %S"prevlast|(prev,_)::rem->Format.fprintfppf"%S , %a"prevorpatreminletunexpected=Format.asprintf"string value %S"sinletexpected=Format.asprintf"%a"orpatcasesinraise(Cannot_destruct([],Unexpected(unexpected,expected)))))~schemastringletdefid?title?descriptionencoding=Describe{id;title;description;encoding}letassoc:typet.tencoding->(string*t)listencoding=funt->Ezjsonm_encoding.custom(funl->`O(List.map(fun(n,v)->n,Ezjsonm_encoding.constructtv)l))(funv->matchvwith|`Ol->letdestructntv=tryEzjsonm_encoding.destructtvwithCannot_destruct(p,exn)->raise(Cannot_destruct(`Fieldn::p,exn))inList.map(fun(n,v)->n,destructntv)l|#Json_repr.ezjsonmask->raise(unexpectedk"asssociative object"))~schema:(lets=schematinJson_schema.(update(element(Object{object_specswithadditional_properties=Some(roots)}))s))letrecis_nullable:typet.tencoding->bool=function|Constant_->false|Int_->false|Float_->false|Array_->false|Empty->false|String->false|Bool->false|Obj_->false|Tup_->false|Objs_->false|Tups_->false|Null->true|Ignore->true|Option_->true|Conv(_,_,t,_)->is_nullablet|Unioncases->List.exists(fun(Case{encoding=t})->is_nullablet)cases|Describe{encoding=t}->is_nullablet|Mu{self}asenc->is_nullable(selfenc)|Custom(_,sch)->Json_schema.is_nullableschletoption:typet.tencoding->toptionencoding=funt->ifis_nullablettheninvalid_arg"Json_encoding.option: cannot nest nullable encodings";Optiontletany_value=letreadreprv=Json_repr.repr_to_anyreprvinletwritereprv=Json_repr.any_to_reprreprvinCustom({read;write},Json_schema.any)letany_ezjson_value=letreadreprv=Json_repr.convertrepr(moduleJson_repr.Ezjsonm)vinletwritereprv=Json_repr.convert(moduleJson_repr.Ezjsonm)reprvinCustom({read;write},Json_schema.any)letany_document=letread:typett.(moduleJson_repr.Reprwithtypevalue=tt)->tt->Json_repr.any=fun(moduleRepr)v->matchRepr.viewvwith|`A_|`O_->Json_repr.repr_to_any(moduleRepr)v|k->raise@@unexpectedk"array or object"inletwritereprv=Json_repr.any_to_reprreprvinCustom({read;write},Json_schema.any)letany_schema=Ezjsonm_encoding.customJson_schema.to_json(funj->tryJson_schema.of_jsonjwitherr->raise(Cannot_destruct([],Bad_schemaerr)))~schema:Json_schema.selfletmerge_tupst1t2=letrecis_tup:typet.tencoding->bool=function|Tup_->true|Tups_(* by construction *)->true|Conv(_,_,t,None)->is_tupt|Mu{self}asenc->is_tup(selfenc)|Describe{encoding=t}->is_tupt|_->falseinifis_tupt1&&is_tupt2thenTups(t1,t2)elseinvalid_arg"Json_encoding.merge_tups"letlistt=Conv(Array.of_list,Array.to_list,Arrayt,None)letmerge_objso1o2=(* FIXME: check fields unicity *)letrecis_obj:typet.tencoding->bool=function|Obj_->true|Objs_(* by construction *)->true|Conv(_,_,t,None)->is_objt|Empty->true|Ignore->true|Unioncases->List.for_all(fun(Case{encoding=o})->is_objo)cases|Mu{self}asenc->is_obj(selfenc)|Describe{encoding=t}->is_objt|_->falseinifis_objo1&&is_objo2thenObjs(o1,o2)elseinvalid_arg"Json_encoding.merge_objs"letempty=Emptyletunit=Ignoreletcase?title?descriptionencodingprojinj=Case{encoding;proj;inj;title;description}letunion=function|[]->invalid_arg"Json_encoding.union"|cases->(* FIXME: check mutual exclusion *)Unioncasesletrecprint_error?print_unknownppf=function|Cannot_destruct([],exn)->print_error?print_unknownppfexn|Cannot_destruct(path,Unexpected(unex,ex))->Format.fprintfppf"At %a, unexpected %s instead of %s"(Json_query.print_path_as_json_path~wildcards:true)pathunexex|Cannot_destruct(path,No_case_matchederrs)->Format.fprintfppf"@[<v 2>At %a, no case matched:@,%a@]"(Json_query.print_path_as_json_path~wildcards:true)path(Format.pp_print_list(print_error?print_unknown))errs|Cannot_destruct(path,Bad_array_size(unex,ex))->Format.fprintfppf"At %a, unexpected array of size %d instead of %d"(Json_query.print_path_as_json_path~wildcards:true)pathunexex|Cannot_destruct(path,Missing_fieldn)->Format.fprintfppf"At %a, missing object field %s"(Json_query.print_path_as_json_path~wildcards:true)pathn|Cannot_destruct(path,Unexpected_fieldn)->Format.fprintfppf"At %a, unexpected object field %s"(Json_query.print_path_as_json_path~wildcards:true)pathn|Cannot_destruct(path,Bad_schemaexn)->Format.fprintfppf"@[<v 2>At %a, bad custom schema:@,%a@]"(Json_query.print_path_as_json_path~wildcards:true)path(print_error?print_unknown)exn|Unexpected(unex,ex)->Format.fprintfppf"Unexpected %s instead of %s"unexex|No_case_matchederrs->Format.fprintfppf"@[<v 2>No case matched:@,%a@]"(Format.pp_print_list(print_error?print_unknown))errs|Bad_array_size(unex,ex)->Format.fprintfppf"Unexpected array of size %d instead of %d"unexex|Missing_fieldn->Format.fprintfppf"Missing object field %s"n|Unexpected_fieldn->Format.fprintfppf"Unexpected object field %s"n|Bad_schemaexn->Format.fprintfppf"@[<v 2>bad custom schema:@,%a@]"(print_error?print_unknown)exn|Cannot_destruct(path,exn)->Format.fprintfppf"@[<v 2>At %a:@,%a@]"(Json_query.print_path_as_json_path~wildcards:true)path(print_error?print_unknown)exn|exn->Json_schema.print_error?print_unknownppfexnincludeEzjsonm_encoding