123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930openPpxlibmoduleB=Ast_builder.DefaultmoduleString_map=Map.Make(String)typemetadata={description:stringoption;min_items:intoption;max_items:intoption;unique_items:booloption;min_length:intoption;max_length:intoption;pattern:stringoption;minimum:stringoption;maximum:stringoption;}typerun_shape={env_type:core_typeoption;input_type:core_type;output_type:core_type;returns_result:bool;}letempty_metadata={description=None;min_items=None;max_items=None;unique_items=None;min_length=None;max_length=None;pattern=None;minimum=None;maximum=None;}letraise_errorf~locfmt=Location.raise_errorf~locfmtletlident~locname=B.Located.mk~loc(Longident.Lidentname)letlid~locname=B.Located.mk~loc(Longident.parsename)letevar~locname=B.pexp_ident~loc(lid~locname)letpvar~locname=B.ppat_var~loc(B.Located.mk~locname)letestring~locvalue=B.estring~locvalueleteint~locvalue=B.eint~locvalueletebool~locvalue=B.ebool~locvalueleteunit~loc=[%expr()]letsome~locexpr=B.pexp_construct~loc(lident~loc"Some")(Someexpr)letnone~loc=B.pexp_construct~loc(lident~loc"None")Nonelettrim_docvalue=String.trimvalueletpayload_string=function|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_string(value,_,_));_},_);_;};]->Somevalue|_->Noneletpayload_int=function|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_integer(value,_));_},_);_;};]->int_of_string_optvalue|_->Noneletpayload_number_string=function|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_integer(value,_));_},_);_;};]->Some(value^".")|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_float(value,_));_},_);_;};]->Somevalue|_->Noneletpayload_bool=function|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_construct({txt=Longident.Lident(("true"|"false")asvalue);_;},None);_;},_);_;};]->Some(String.equalvalue"true")|_->Noneletdoc_from_attrsattrs=attrs|>List.find_map(funattr->matchattr.attr_name.txtwith|"ocaml.doc"|"description"|"schema.description"|"doc"->Option.maptrim_doc(payload_stringattr.attr_payload)|_->None)letmerge_metadatafirstsecond={description=(matchfirst.descriptionwith|Some_->first.description|None->second.description);min_items=(matchfirst.min_itemswith|Some_->first.min_items|None->second.min_items);max_items=(matchfirst.max_itemswith|Some_->first.max_items|None->second.max_items);unique_items=(matchfirst.unique_itemswith|Some_->first.unique_items|None->second.unique_items);min_length=(matchfirst.min_lengthwith|Some_->first.min_length|None->second.min_length);max_length=(matchfirst.max_lengthwith|Some_->first.max_length|None->second.max_length);pattern=(matchfirst.patternwith|Some_->first.pattern|None->second.pattern);minimum=(matchfirst.minimumwith|Some_->first.minimum|None->second.minimum);maximum=(matchfirst.maximumwith|Some_->first.maximum|None->second.maximum);}letmetadata_from_attrsattrs=List.fold_left(funmetadataattr->matchattr.attr_name.txtwith|"ocaml.doc"|"description"|"schema.description"|"doc"->{metadatawithdescription=Option.maptrim_doc(payload_stringattr.attr_payload);}|"min_items"|"schema.min_items"->{metadatawithmin_items=payload_intattr.attr_payload}|"max_items"|"schema.max_items"->{metadatawithmax_items=payload_intattr.attr_payload}|"unique_items"|"schema.unique_items"->{metadatawithunique_items=payload_boolattr.attr_payload}|"min_length"|"schema.min_length"->{metadatawithmin_length=payload_intattr.attr_payload}|"max_length"|"schema.max_length"->{metadatawithmax_length=payload_intattr.attr_payload}|"pattern"|"schema.pattern"->{metadatawithpattern=payload_stringattr.attr_payload}|"minimum"|"schema.minimum"->{metadatawithminimum=payload_number_stringattr.attr_payload}|"maximum"|"schema.maximum"->{metadatawithmaximum=payload_number_stringattr.attr_payload}|_->metadata)empty_metadataattrsletmetadata_of_field(field:label_declaration)=merge_metadata(metadata_from_attrsfield.pld_attributes)(metadata_from_attrsfield.pld_type.ptyp_attributes)lethas_metadatametadata=Option.is_somemetadata.description||Option.is_somemetadata.min_items||Option.is_somemetadata.max_items||Option.is_somemetadata.unique_items||Option.is_somemetadata.min_length||Option.is_somemetadata.max_length||Option.is_somemetadata.pattern||Option.is_somemetadata.minimum||Option.is_somemetadata.maximumletpexp_float~locvalue=B.pexp_constant~loc(Pconst_float(value,None))letcall~locnameargs=B.pexp_apply~loc(evar~locname)argsletapply_metadata~locmetadatacodec=ifnot(has_metadatametadata)thencodecelseletschema=evar~loc"schema"inletschema=matchmetadata.descriptionwith|None->schema|Somedescription->call~loc"Chatoyant.Schema.Schema.with_description"[(Nolabel,some~loc(estring~locdescription));(Nolabel,schema);]inletschema=letargs=[Option.map(funvalue->(Labelled"min_items",eint~locvalue))metadata.min_items;Option.map(funvalue->(Labelled"max_items",eint~locvalue))metadata.max_items;Option.map(funvalue->(Labelled"unique_items",ebool~locvalue))metadata.unique_items;]|>List.filter_mapFun.idinifargs=[]thenschemaelsecall~loc"Chatoyant.Schema.Schema.with_array_constraints"(args@[(Nolabel,schema)])inletschema=letargs=[Option.map(funvalue->(Labelled"min_length",eint~locvalue))metadata.min_length;Option.map(funvalue->(Labelled"max_length",eint~locvalue))metadata.max_length;Option.map(funvalue->(Labelled"pattern",estring~locvalue))metadata.pattern;]|>List.filter_mapFun.idinifargs=[]thenschemaelsecall~loc"Chatoyant.Schema.Schema.with_string_constraints"(args@[(Nolabel,schema)])inletschema=letargs=[Option.map(funvalue->(Labelled"minimum",pexp_float~locvalue))metadata.minimum;Option.map(funvalue->(Labelled"maximum",pexp_float~locvalue))metadata.maximum;]|>List.filter_mapFun.idinifargs=[]thenschemaelsecall~loc"Chatoyant.Schema.Schema.with_number_constraints"(args@[(Nolabel,schema)])in[%exprChatoyant.Schema.Codec.map_schema(funschema->[%eschema])[%ecodec]]letcodec_nametype_name="__chatoyant_codec_"^type_nameletconstructor_json_namename=letbuffer=Buffer.create(String.lengthname+4)inString.iteri(funindexch->ifch='_'thenBuffer.add_charbuffer'_'elseifChar.uppercase_asciich=ch&&Char.lowercase_asciich<>chthen(ifindex>0thenBuffer.add_charbuffer'_';Buffer.add_charbuffer(Char.lowercase_asciich))elseBuffer.add_charbufferch)name;Buffer.contentsbufferlettool_name_from_modulename=letraw=constructor_json_namenameinifString.lengthraw>0&&raw.[0]='_'thenString.subraw1(String.lengthraw-1)elserawlettype_name_of_core_type=function|{ptyp_desc=Ptyp_constr({txt=Longident.Lidentname;_},[]);_}->Somename|_->Noneletrecappend_codec_suffix=function|Longident.Lidentname->Longident.Lident(name^"_codec")|Longident.Ldot(parent,name)->Longident.Ldot(parent,name^"_codec")|Longident.Lapply_->Location.raise_errorf"chatoyant.ppx cannot derive a codec path for this type"letreccodec_expr_of_type~type_namestyp=letloc=typ.ptyp_locinletcodec=matchtyp.ptyp_descwith|Ptyp_constr({txt=Longident.Lident"string";_},[])->[%exprChatoyant.Schema.Codec.string()]|Ptyp_constr({txt=Longident.Lident"float";_},[])->[%exprChatoyant.Schema.Codec.float()]|Ptyp_constr({txt=Longident.Lident"int";_},[])->[%exprChatoyant.Schema.Codec.int()]|Ptyp_constr({txt=Longident.Lident"bool";_},[])->[%exprChatoyant.Schema.Codec.bool()]|Ptyp_constr({txt=Longident.Lident"list";_},[item])|Ptyp_constr({txt=Longident.Ldot(Longident.Lident"List","t");_},[item])->[%exprChatoyant.Schema.Codec.array[%ecodec_expr_of_type~type_namesitem]]|Ptyp_constr({txt=Longident.Lident"option";_},[item])|Ptyp_constr({txt=Longident.Ldot(Longident.Lident"Option","t");_},[item])->[%exprChatoyant.Schema.Codec.optional[%ecodec_expr_of_type~type_namesitem]]|Ptyp_constr({txt=Longident.Lidentname;_},[])whenString_map.memnametype_names->call~loc(codec_namename)[(Nolabel,eunit~loc)]|Ptyp_constr({txt=Longident.Lidentname;_},[])->call~loc(name^"_codec")[(Nolabel,eunit~loc)]|Ptyp_constr({txt=Longident.Ldot_asname;_},[])->B.pexp_apply~loc(B.pexp_ident~loc(B.Located.mk~loc(append_codec_suffixname)))[(Nolabel,eunit~loc)]|_->raise_errorf~loc"chatoyant.ppx cannot derive a JSON codec for this type yet; use a \
local record/variant or string/int/float/bool/list/option"inapply_metadata~loc(metadata_from_attrstyp.ptyp_attributes)codeclettuple2~locleftright=B.pexp_tuple~loc[left;right]letfield_codec_expr~type_names(field:label_declaration)=letbase=codec_expr_of_type~type_namesfield.pld_typeinapply_metadata~loc:field.pld_loc(metadata_of_fieldfield)baseletschema_field_expr~type_namesfield=letloc=field.pld_locintuple2~loc(estring~locfield.pld_name.txt)[%exprChatoyant.Schema.Codec.schema[%efield_codec_expr~type_namesfield]]letrecord_field_access~locvaluefield_name=B.pexp_field~locvalue(lident~locfield_name)letencode_field_expr~type_namesfield=letloc=field.pld_locinletvalue=evar~loc"value"intuple2~loc(estring~locfield.pld_name.txt)[%exprChatoyant.Schema.Codec.encode[%efield_codec_expr~type_namesfield][%erecord_field_access~locvaluefield.pld_name.txt]]letbind_result~locrhsnamebody=[%exprmatch[%erhs]with|Error__chatoyant_error->Error__chatoyant_error|Ok[%ppvar~locname]->[%ebody]]letdecode_field_binding~type_namesfieldbody=letloc=field.pld_locinletrhs=[%exprChatoyant.Schema.Codec.decode_field[%eestring~locfield.pld_name.txt][%efield_codec_expr~type_namesfield]json]inbind_result~locrhsfield.pld_name.txtbodyletlocal_type~loctype_name=B.ptyp_constr~loc(lident~loctype_name)[]letrecord_expr~loc~type_namefields=B.pexp_constraint~loc(B.pexp_record~loc(List.map(fun(field:label_declaration)->(lident~locfield.pld_name.txt,evar~locfield.pld_name.txt))fields)None)(local_type~loctype_name)letrecord_codec_body~type_names~type_name?descriptionfields=letloc=type_name.locinList.iter(fun(field:label_declaration)->matchfield.pld_mutablewith|Mutable->raise_errorf~loc:field.pld_loc"chatoyant.ppx tool types must use immutable record fields"|Immutable->())fields;letdecode_body=List.fold_right(decode_field_binding~type_names)fields[%exprOk[%erecord_expr~loc~type_name:type_name.txtfields]]inletschema=letargs=(matchdescriptionwith|None->[]|Somedescription->[(Optional"description",some~loc(estring~locdescription))])@[(Nolabel,B.elist~loc(List.map(schema_field_expr~type_names)fields));]incall~loc"Chatoyant.Schema.Schema.object_"argsin[%exprChatoyant.Schema.Codec.custom~schema:[%eschema]~encode:(fun(value:[%tlocal_type~loctype_name.txt])->Chatoyant.Runtime.Json.Object[%eB.elist~loc(List.map(encode_field_expr~type_names)fields)])~decode:(funjson->[%edecode_body])]letvariant_codec_body~type_name?descriptionconstructors=letloc=type_name.locinletcases=List.map(fun(constructor:constructor_declaration)->matchconstructor.pcd_argswith|Pcstr_tuple[]->letjson_name=constructor_json_nameconstructor.pcd_name.txtin(constructor,json_name)|_->raise_errorf~loc:constructor.pcd_loc"chatoyant.ppx only supports nullary variant constructors for \
JSON enum tool types")constructorsinletencode_cases=List.map(fun(constructor,json_name)->B.case~lhs:(B.ppat_construct~loc(lident~locconstructor.pcd_name.txt)None)~guard:None~rhs:[%exprChatoyant.Runtime.Json.String[%eestring~locjson_name]])casesinletdecode_cases=List.map(fun(constructor,json_name)->B.case~lhs:[%pat?Chatoyant.Runtime.Json.Stringvalue]~guard:(Some[%exprString.equalvalue[%eestring~locjson_name]])~rhs:[%exprOk[%eB.pexp_construct~loc(lident~locconstructor.pcd_name.txt)None]])casesinletdecode_cases=decode_cases@[B.case~lhs:[%pat?Chatoyant.Runtime.Json.Stringvalue]~guard:None~rhs:[%exprError("unexpected "^[%eestring~loctype_name.txt]^" value: "^value)];B.case~lhs:[%pat?_]~guard:None~rhs:[%exprError("expected "^[%eestring~loctype_name.txt]^" string")];]inletschema=letvalues=B.elist~loc(List.map(fun(_,json_name)->[%exprChatoyant.Runtime.Json.String[%eestring~locjson_name]])cases)inletargs=(matchdescriptionwith|None->[]|Somedescription->[(Optional"description",some~loc(estring~locdescription))])@[(Nolabel,values)]incall~loc"Chatoyant.Schema.Schema.enum"argsin[%exprChatoyant.Schema.Codec.custom~schema:[%eschema]~encode:(fun__chatoyant_variant->[%eB.pexp_match~loc[%expr__chatoyant_variant]encode_cases])~decode:(fun__chatoyant_json->[%eB.pexp_match~loc[%expr__chatoyant_json]decode_cases])]letalias_codec_body~type_names~metadatatyp=codec_expr_of_type~type_namestyp|>apply_metadata~loc:typ.ptyp_locmetadataletcodec_body_of_decl~type_namesdecl=letmetadata=metadata_from_attrsdecl.ptype_attributesinletdescription=metadata.descriptioninmatch(decl.ptype_kind,decl.ptype_manifest)with|Ptype_recordfields,None->record_codec_body~type_names~type_name:decl.ptype_name?descriptionfields|Ptype_variantconstructors,None->variant_codec_body~type_name:decl.ptype_name?descriptionconstructors|Ptype_abstract,Somemanifest->alias_codec_body~type_names~metadatamanifest|_->raise_errorf~loc:decl.ptype_loc"chatoyant.ppx can derive tool codecs from records, nullary variants, \
and aliases only"letcodec_binding~type_namesdecl=letloc=decl.ptype_locinB.value_binding~loc~pat:(pvar~loc(codec_namedecl.ptype_name.txt))~expr:[%exprfun()->[%ecodec_body_of_decl~type_namesdecl]]lettype_map_of_declstype_decls=List.fold_left(funmapdecl->String_map.adddecl.ptype_name.txtdeclmap)String_map.emptytype_declslethelper_bindingsdecl=letloc=decl.ptype_locinlettype_name=decl.ptype_name.txtinletcodec=call~loc(codec_nametype_name)[(Nolabel,eunit~loc)]in[B.value_binding~loc~pat:(pvar~loc(type_name^"_codec"))~expr:[%exprfun()->[%ecodec]];B.value_binding~loc~pat:(pvar~loc(type_name^"_schema"))~expr:[%exprChatoyant.Schema.Codec.schema[%ecodec]];B.value_binding~loc~pat:(pvar~loc(type_name^"_to_json"))~expr:[%exprfunvalue->Chatoyant.Schema.Codec.encode[%ecodec]value];B.value_binding~loc~pat:(pvar~loc(type_name^"_of_json"))~expr:[%exprfunjson->Chatoyant.Schema.Codec.decode[%ecodec]json];]letgenerated_codec_items~loctype_decls=lettype_names=type_map_of_declstype_declsinletcodec_item=B.pstr_value~locRecursive(List.map(codec_binding~type_names)type_decls)inlethelper_items=type_decls|>List.concat_maphelper_bindings|>funbindings->ifbindings=[]then[]else[B.pstr_value~locNonrecursivebindings]incodec_item::helper_itemslethelper_sig_itemsdecl=letloc=decl.ptype_locinlettype_name=decl.ptype_name.txtinlettyp=local_type~loctype_nameinletvaluenametyp=B.psig_value~loc(B.value_description~loc~name:(B.Located.mk~locname)~type_:typ~prim:[])in[value(type_name^"_codec")[%type:unit->[%ttyp]Chatoyant.Schema.Codec.t];value(type_name^"_schema")[%type:Chatoyant.Schema.Schema.field];value(type_name^"_to_json")[%type:[%ttyp]->Chatoyant.Runtime.Json.t];value(type_name^"_of_json")[%type:Chatoyant.Runtime.Json.t->([%ttyp],string)result];]letgenerated_codec_sig_itemstype_decls=List.concat_maphelper_sig_itemstype_declslettype_decls_of_structurestructure=structure|>List.concat_map(function|{pstr_desc=Pstr_type(_,decls);_}->decls|_->[])letconstructor_arg_types=function|Pcstr_tupleargs->args|Pcstr_recordfields->List.map(funfield->field.pld_type)fieldsletreccollect_reachable_typetype_mapseentyp=letseen=matchtyp.ptyp_descwith|Ptyp_constr({txt=Longident.Lidentname;_},args)->letseen=List.fold_left(collect_reachable_typetype_map)seenargsinifString_map.memnametype_mapthencollect_reachable_decltype_mapseen(String_map.findnametype_map)elseseen|Ptyp_constr(_,args)->List.fold_left(collect_reachable_typetype_map)seenargs|Ptyp_tupletypes->List.fold_left(collect_reachable_typetype_map)seentypes|_->seeninseenandcollect_reachable_decltype_mapseendecl=letname=decl.ptype_name.txtinifString_map.memnameseenthenseenelseletseen=String_map.addnamedeclseeninletseen=matchdecl.ptype_kindwith|Ptype_recordfields->List.fold_left(funseenfield->collect_reachable_typetype_mapseenfield.pld_type)seenfields|Ptype_variantconstructors->List.fold_left(funseenconstructor->constructor_arg_typesconstructor.pcd_args|>List.fold_left(collect_reachable_typetype_map)seen)seenconstructors|_->seeninmatchdecl.ptype_manifestwith|Sometyp->collect_reachable_typetype_mapseentyp|None->seenletreachable_type_declstype_declstypes=lettype_map=type_map_of_declstype_declsinletreachable=List.fold_left(collect_reachable_typetype_map)String_map.emptytypesinList.filter(fundecl->String_map.memdecl.ptype_name.txtreachable)type_declsletrecinsert_after_leading_type_decls~generatedacc=function|({pstr_desc=Pstr_attribute_|Pstr_type_;_}asitem)::rest->insert_after_leading_type_decls~generated(item::acc)rest|rest->List.revacc@generated@restletrun_binding_of_structurestructure=structure|>List.find_map(function|{pstr_desc=Pstr_value(_,bindings);_}->List.find_opt(funbinding->matchbinding.pvb_pat.ppat_descwith|Ppat_var{txt="run";_}->true|_->false)bindings|_->None)letrecarrowsacctyp=matchtyp.ptyp_descwith|Ptyp_arrow(Nolabel,arg,rest)->arrows(arg::acc)rest|Ptyp_arrow((Labelled_|Optional_),_,_)->raise_errorf~loc:typ.ptyp_loc"module%%tool currently expects run to take one request record \
argument or env -> request"|_->(List.revacc,typ)letis_string_type=function|{ptyp_desc=Ptyp_constr({txt=Longident.Lident"string";_},[]);_}->true|_->falseletparse_return_typetyp=matchtyp.ptyp_descwith|Ptyp_constr({txt=Longident.Lident"result";_},[output;error])whenis_string_typeerror->(output,true)|Ptyp_constr({txt=Longident.Ldot(Longident.Lident"Stdlib","result");_},[output;error])whenis_string_typeerror->(output,true)|_->(typ,false)letparse_run_shapebinding=lettyp=matchbinding.pvb_constraintwith|Some(Pvc_constraint{typ;_})->typ|Some(Pvc_coercion_)|None->raise_errorf~loc:binding.pvb_loc"module%%tool requires an explicit type signature on let run"inletargs,return_type=arrows[]typinletoutput_type,returns_result=parse_return_typereturn_typeinmatchargswith|[input_type]->{env_type=None;input_type;output_type;returns_result}|[env_type;input_type]->{env_type=Someenv_type;input_type;output_type;returns_result}|_->raise_errorf~loc:typ.ptyp_loc"module%%tool run must have shape request -> result or env -> request \
-> result"lettool_descriptionmodule_docrun_binding=matchdoc_from_attrsrun_binding.pvb_attributeswith|Somedescriptionwhendescription<>""->description|_->(matchmodule_docwith|Somedescriptionwhendescription<>""->description|_->"Tool generated by Chatoyant.")letmodule_doc_from_structurestructure=structure|>List.find_map(function|{pstr_desc=Pstr_attribute{attr_name={txt="ocaml.text"|"ocaml.doc";_};attr_payload;_;};_;}->Option.maptrim_doc(payload_stringattr_payload)|_->None)letcodec_for_named_or_type~type_namestyp=matchtype_name_of_core_typetypwith|SomenamewhenString_map.memnametype_names->letloc=typ.ptyp_locincall~loc(codec_namename)[(Nolabel,eunit~loc)]|_->codec_expr_of_type~type_namestypletgenerated_tool_item~loc~module_name~description~type_namesshape=letname_expr=estring~loc(tool_name_from_modulemodule_name)inletdescription_expr=estring~locdescriptioninletinput_codec=codec_for_named_or_type~type_namesshape.input_typeinletoutput_codec=codec_for_named_or_type~type_namesshape.output_typeinletbody_no_env=ifshape.returns_resultthen[%exprruninput]else[%exprOk(runinput)]inletbody_with_env=ifshape.returns_resultthen[%exprrunenvinput]else[%exprOk(runenvinput)]inmatchshape.env_typewith|None->[%strilettool=Chatoyant.Core.Tool.create_typed~name:[%ename_expr]~description:[%edescription_expr]~args:[%einput_codec]~result:[%eoutput_codec](fun_contextinput->[%ebody_no_env])]|Some_->[%striletmakeenv=Chatoyant.Core.Tool.create_typed~name:[%ename_expr]~description:[%edescription_expr]~args:[%einput_codec]~result:[%eoutput_codec](fun_contextinput->[%ebody_with_env])]letexpand_tool_module~locbinding=letmodule_name=matchbinding.pmb_name.txtwith|Somename->name|None->raise_errorf~loc:binding.pmb_loc"module%%tool requires a named module"inmatchbinding.pmb_expr.pmod_descwith|Pmod_structurestructure->lettype_decls=type_decls_of_structurestructureinletrun_binding=matchrun_binding_of_structurestructurewith|Somebinding->binding|None->raise_errorf~loc:binding.pmb_loc"module%%tool requires a let run binding"inletshape=parse_run_shaperun_bindinginlettype_decls=reachable_type_declstype_decls[shape.input_type;shape.output_type]inlettype_names=type_map_of_declstype_declsinletdescription=tool_description(module_doc_from_structurestructure)run_bindinginletcodec_items=iftype_decls=[]then[]elsegenerated_codec_items~loctype_declsinlettool_item=generated_tool_item~loc~module_name~description~type_namesshapeinletstructure=insert_after_leading_type_decls~generated:codec_items[]structurein{bindingwithpmb_expr={binding.pmb_exprwithpmod_desc=Pmod_structure(structure@[tool_item]);};}|_->raise_errorf~loc:binding.pmb_loc"module%%tool expects a struct ... end module body"letreclast_module_name=function|Longident.Lidentname->Somename|Longident.Ldot(_,name)->Somename|Longident.Lapply_->Noneletdefault_data_name=function|Longident.Lidentname->constructor_json_namename|Longident.Ldot(parent,"t")->(matchlast_module_nameparentwith|Somename->tool_name_from_modulename|None->"data")|Longident.Ldot(_,name)->constructor_json_namename|Longident.Lapply_->"data"letcodec_call_of_payload_typetyp=letloc=typ.ptyp_locinmatchtyp.ptyp_descwith|Ptyp_constr({txt=type_lid;_},[])->letcodec_lid=append_codec_suffixtype_lidinB.pexp_apply~loc(B.pexp_ident~loc(B.Located.mk~loccodec_lid))[(Nolabel,eunit~loc)]|_->raise_errorf~loc"%%chatoyant.gen_data expects a concrete type name, such as \
[%%chatoyant.gen_data: invoice] or [%%chatoyant.gen_data: Product.t]"letexpand_gen_data_expression~loctyp=lettype_lid=matchtyp.ptyp_descwith|Ptyp_constr({txt;_},[])->txt|_->raise_errorf~loc:typ.ptyp_loc"%%chatoyant.gen_data expects a concrete type name"inB.pexp_apply~loc(evar~loc"Chatoyant.gen_data")[(Labelled"name",estring~loc(default_data_nametype_lid));(Labelled"codec",codec_call_of_payload_typetyp);]letexpand_structure_itemitem=matchitem.pstr_descwith|Pstr_extension(({txt="tool"|"chatoyant.tool";loc},PStr[{pstr_desc=Pstr_modulebinding;_}]),_)->letbinding=expand_tool_module~locbindingin{itemwithpstr_desc=Pstr_modulebinding}|_->itemletmapper=objectinheritAst_traverse.mapassupermethod!structure_itemitem=matchitem.pstr_descwith|Pstr_extension(({txt="tool"|"chatoyant.tool";_},PStr[{pstr_desc=Pstr_module_;_}]),_)->expand_structure_itemitem|_->super#structure_itemitemmethod!expressionexpr=matchexpr.pexp_descwith|Pexp_extension({txt="chatoyant.gen_data"|"gen_data";loc},PTyptyp)->expand_gen_data_expression~loctyp|_->super#expressionexprendletimplstructure=mapper#structurestructureletstr_type_decl=Deriving.Generator.make_noarg(fun~loc~path:_(_rec_flag,decls)->generated_codec_items~locdecls)letsig_type_decl=Deriving.Generator.make_noarg(fun~loc:_~path:_(_rec_flag,decls)->generated_codec_sig_itemsdecls)let(_:Deriving.t)=Deriving.add"chatoyant"~str_type_decl~sig_type_decllet()=Driver.register_transformation"chatoyant.ppx"~impl