123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390(*********************************************************************************)(* Ocf *)(* *)(* Copyright (C) 2015-2024 INRIA. All rights reserved. *)(* *)(* This program 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, version 3 of the License. *)(* *)(* This program 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 Library General Public License for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public *)(* License along with this program; if not, write to the Free Software *)(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA *)(* 02111-1307 USA *)(* *)(* As a special exception, you have permission to link this program *)(* with the OCaml compiler and distribute executables, as long as you *)(* follow the requirements of the GNU GPL in regard to all of the *)(* software in the executable aside from the OCaml compiler. *)(* *)(* Contact: Maxence.Guesdon@inria.fr *)(* *)(*********************************************************************************)moduleSMap=Map.Make(String)typepath=stringlistletstring_of_path=String.concat"."typeerror=|Json_errorofstring|Invalid_valueofYojson.Safe.t|Invalid_pathofpath|Path_conflictofpath|Error_at_pathofpath*error|Exn_at_pathofpath*exnexceptionErroroferrorletrecstring_of_error=function|Json_errormsg->Printf.sprintf"Error while reading JSON: %s"msg|Invalid_valuejson->Printf.sprintf"Invalid value %s"(Yojson.Safe.pretty_to_stringjson)|Invalid_pathp->Printf.sprintf"Invalid path %S"(string_of_pathp)|Path_conflictp->Printf.sprintf"Path conflict on %S"(string_of_pathp)|Error_at_path(p,e)->Printf.sprintf"%S: %s"(string_of_pathp)(string_of_errore)|Exn_at_path(p,e)->Printf.sprintf"%S: %s"(string_of_pathp)(Printexc.to_stringe)leterrore=raise(Errore)letjson_errors=error(Json_errors)letinvalid_values=error(Invalid_values)letinvalid_pathp=error(Invalid_pathp)letpath_conflictp=error(Path_conflictp)leterror_at_pathpe=error(Error_at_path(p,e))letexn_at_pathpe=error(Exn_at_path(p,e))let()=Printexc.register_printer(functionErrore->Some(string_of_errore)|_->None)moduleWrapper=structtype'at={to_json:?with_doc:bool->'a->Yojson.Safe.t;from_json:?def:'a->Yojson.Safe.t->'a;}letmaketo_jsonfrom_json={to_json;from_json}letof_ok_errorfjson=matchfjsonwith`Okx->x|`Errormsg->invalid_valuejsonletjson=letto_j?with_docjson=jsoninletfrom_j?defjson=jsoninmaketo_jfrom_jletint=letto_j?with_docn=`Intninletfrom_j?def=function`Intn->n|(`Intlits)|(`Strings)asjson->begintryint_of_stringswith_->invalid_valuejsonend|json->invalid_valuejsoninmaketo_jfrom_jletfloat=letto_j?with_docx=`Floatxinletfrom_j?def=function`Floatx->x|`Intn->floatn|(`Intlits)|(`Strings)asjson->begintryfloat_of_stringswith_->invalid_valuejsonend|json->invalid_valuejsoninmaketo_jfrom_jletbool=letto_j?with_docx=`Boolxinletfrom_j?def=function`Boolx->x|json->invalid_valuejsoninmaketo_jfrom_jletstring_to_json?with_docx=`Stringxletstring_from_json?def=function|`Intlits|`Strings->s|`Intn->string_of_intn|json->invalid_valuejsonletstring=makestring_to_jsonstring_from_jsonletstring_to_strfrom_str=letto_j?with_docx=string_to_json?with_doc(to_strx)inletfrom_j?defx=from_str(string_from_jsonx)inmaketo_jfrom_jletlistw=letto_j?with_docl=`List(List.map(w.to_json?with_doc)l)inletfrom_j?def=function|`Listl->List.map(w.from_json?def:None)l|`Null->[]|json->invalid_valuejsoninmaketo_jfrom_jletoptionw=letto_j?with_doc=function|None->`Null|Somex->w.to_json?with_docxinletfrom_j?def=function`Null->None|x->Some(w.from_jsonx)inmaketo_jfrom_jletpairw1w2=letto_j?with_doc(v1,v2)=`List[w1.to_json?with_docv1;w2.to_json?with_docv2]inletfrom_j?def=function|`List[v1;v2]->(w1.from_jsonv1,w2.from_jsonv2)|json->invalid_valuejsoninmaketo_jfrom_jlettriplew1w2w3=letto_j?with_doc(v1,v2,v3)=`List[w1.to_json?with_docv1;w2.to_json?with_docv2;w3.to_json?with_docv3;]inletfrom_j?def=function`List[v1;v2;v3]->(w1.from_jsonv1,w2.from_jsonv2,w3.from_jsonv3)|json->invalid_valuejsoninmaketo_jfrom_jtypeassocs=(string*Yojson.Safe.t)listletstring_map~fold~add~emptyw=letto_j?with_docmap=letl=fold(funkvacc->(k,w.to_json?with_docv)::acc)map[]in`Assoclinletfrom_j?def=function|`Assocl->List.fold_left(funmap(k,v)->addk(w.from_jsonv)map)emptyl|json->invalid_valuejsoninmaketo_jfrom_jendtype'awrapper='aWrapper.ttypeconf_option_={wrapper:'a.'awrapper;mutablevalue:'a.'a;doc:stringoption;cb:'a.('a->unit)option;}type'aconf_option=conf_option_letgeto=o.valueletset(o:'aconf_option)(v:'a)=o.value<-Obj.magicv;matcho.cbwith|None->()|Somef->fvletoption:?doc:string->?cb:('a->unit)->'awrapper->'a->'aconf_option=fun?doc?cbwrappervalue->letwrapper=Obj.magicwrapperinletcb=Obj.magiccbin{wrapper=wrapper;value=Obj.magicvalue;doc;cb=cb;}letjson?doc?cbn=option?doc?cbWrapper.jsonnletint?doc?cbn=option?doc?cbWrapper.intnletfloat?doc?cbx=option?doc?cbWrapper.floatxletbool?doc?cbx=option?doc?cbWrapper.boolxletstring?doc?cbs=option?doc?cbWrapper.stringsletlist?doc?cbwl=option?doc?cb(Wrapper.listw)lletoption_?doc?cbwl=option?doc?cb(Wrapper.optionw)lletpair?doc?cbw1w2x=option?doc?cb(Wrapper.pairw1w2)xlettriple?doc?cbw1w2w3x=option?doc?cb(Wrapper.triplew1w2w3)xletstring_map?doc?cb~fold~add~emptywx=option?doc?cb(Wrapper.string_map~fold~add~emptyw)xtypenode=|Optionofconf_option_|GroupofnodeSMap.tand'agroup=nodeletgroup=GroupSMap.emptyletrecadd?(acc_path=[])grouppathnode=matchpathwith[]->invalid_path[]|[h]->beginmatchSMap.findhgroupwith|exceptionNot_found->SMap.addhnodegroup|_->path_conflict(List.rev(h::acc_path))end|h::q->matchSMap.findhgroupwith|exceptionNot_found->letmap=add~acc_path:(h::acc_path)SMap.emptyqnodeinSMap.addh(Groupmap)group|Option_->path_conflict(List.rev(h::acc_path))|Group_whenq=[]->path_conflict(List.rev(h::acc_path))|Groupmap->letmap=add~acc_path:(h::acc_path)mapqnodeinSMap.addh(Groupmap)groupletadd_groupgrouppathg=matchgroupwithOption_->assertfalse|Groupmap->Group(add?acc_path:Nonemappathg)letaddgrouppathoption=matchgroupwith|Option_->assertfalse|Groupmap->Group(add?acc_path:Nonemappath(Optionoption))letas_groupo=Optionoletfrom_json_optionpathoptionjson=tryletv=option.wrapper.Wrapper.from_json~def:option.valuejsoninsetoptionvwithErrore->error_at_pathpathe|e->exn_at_pathpatheletrecfrom_json_group=letfpathassocsstrnode=matchSMap.findstrassocswith|exceptionNot_found->()|json->matchnodewithOptiono->from_json_option(List.rev(str::path))ojson|Groupmap->from_json_group~path:(str::path)mapjsoninfun?(path=[])mapjson->matchjsonwith`Assocassocs->letassocs=List.fold_left(funacc(k,v)->SMap.addkvacc)SMap.emptyassocsinSMap.iter(fpathassocs)map|_->invalid_valuejsonletfrom_json=functionOptiono->from_json_option[]o|Groupg->from_json_group?path:Nonegletfrom_stringmapstr=tryletjson=Yojson.Safe.from_stringstrinfrom_jsonmapjsonwithYojson.Json_errormsg->json_errormsgletfrom_file?(fail_if_not_exist=false)mapfile=tryifSys.file_existsfilethenletjson=Yojson.Safe.from_filefileinfrom_jsonmapjsonelseiffail_if_not_existthenraise(Sys_error(Printf.sprintf"No file %S"file))else()withYojson.Json_errormsg->json_error(Printf.sprintf"%s: %s"filemsg)letto_json_option?with_docoption=option.wrapper.Wrapper.to_json?with_docoption.valueletrecto_json_group?with_docmap=letfnamenodeacc=matchnodewith|Groupmap->(name,to_json_group?with_docmap)::acc|Optiono->letacc=(name,to_json_option?with_doco)::accinmatchwith_doc,o.docwith|Sometrue,Somestr->(name,`Stringstr)::acc|_,_->accin`Assoc(SMap.foldfmap[])letto_json?(with_doc=true)=function|Optiono->to_json_option~with_doco|Groupg->to_json_group~with_docgletto_string?with_docmap=Yojson.Safe.pretty_to_string(to_json?with_docmap)letto_file?with_docmapfile=letoc=open_outfileinYojson.Safe.pretty_to_channeloc(to_json?with_docmap);close_outocletto_argoption?dockey=letdoc=matchdoc,option.docwithSomes,_|None,Somes->"... "^s|None,None->""inletfstr=tryletjson=tryYojson.Safe.from_stringstrwithYojson.Json_errormsg->tryYojson.Safe.from_string(Printf.sprintf"%S"str)withYojson.Json_error_->json_errormsginfrom_json_option[key]optionjsonwithErrore->letmsg=matchewith|Json_errormsg->Printf.sprintf"%s: %s"keymsg|_->string_of_erroreinraise(Arg.Badmsg)in(key,Arg.Stringf,doc)