123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334moduleOt=Pb_codegen_ocaml_typemoduleF=Pb_codegen_formattingletsp=Pb_codegen_util.spletunsupportedjson_label=failwith(sp"Unsupported field type for field: %s"json_label)letruntime_function_for_basic_typejson_labelbasic_typepk=matchbasic_type,pkwith(* String *)|Ot.Bt_string,_->"make_string",None(* Float *)|Ot.Bt_float,Ot.Pk_bits32->"make_float",None|Ot.Bt_float,Ot.Pk_bits64->"make_string",Some"string_of_float"(* Int32 *)|Ot.Bt_int32,Ot.Pk_varint_|Ot.Bt_int32,Ot.Pk_bits32->"make_int",Some"Int32.to_int"(* Uint32 *)|Ot.Bt_uint32,Ot.Pk_varint_|Ot.Bt_uint32,Ot.Pk_bits32->"make_int",Some"(fun (`unsigned x) -> Int32.to_int x)"(* Int64 *)|Ot.Bt_int64,Ot.Pk_varint_|Ot.Bt_int64,Ot.Pk_bits64->"make_string",Some"Int64.to_string"(* Uint64 *)|Ot.Bt_uint64,Ot.Pk_varint_|Ot.Bt_uint64,Ot.Pk_bits64->"make_string",Some"(fun (`unsigned x) -> Int64.to_string x)"(* 64 bit integer are always encoded as string since
only support up to 51 bits integer. An improvement
could be to check for value > 2^51 and use int *)(* int *)|Ot.Bt_int,Ot.Pk_bits32->"make_int",None|Ot.Bt_int,Ot.Pk_varint_|Ot.Bt_int,Ot.Pk_bits64->"make_string",Some"string_of_int"(* bool *)|Ot.Bt_bool,Ot.Pk_varint_->"make_bool",None(* bytes *)|Ot.Bt_bytes,Ot.Pk_bytes->"make_bytes",None|_->unsupportedjson_label(* TODO Wrapper: add a runtime_function_for_wrapper_type which will
return the runtime function name which accepts an option field
and return the YoJson value (ie Null when value is None *)letgen_fieldvar_namejson_labelfield_typepk:stringoption=matchfield_type,pkwith|Ot.Ft_unit,_->None(* Basic types *)|Ot.Ft_basic_typebasic_type,_->letruntime_f,map_function=runtime_function_for_basic_typejson_labelbasic_typepkin(matchmap_functionwith|None->Some(sp"(\"%s\", Pbrt_yojson.%s %s)"json_labelruntime_fvar_name)|Somemap_function->Some(sp"(\"%s\", Pbrt_yojson.%s (%s %s))"json_labelruntime_fmap_functionvar_name))(* TODO Wrapper: add similar case for Ft_wrapper_type but calling a
a different runtime function *)(* User defined *)|Ot.Ft_user_defined_typeudt,_->letf_name=letfunction_prefix="encode_json"inPb_codegen_util.function_name_of_user_defined~function_prefixudtinSome(sp"(\"%s\", %s %s)"json_labelf_namevar_name)|_->assertfalseletgen_rft_nolabelscrf_label(field_type,_,pk)=letvar_name=sp"v.%s"rf_labelinletjson_label=Pb_codegen_util.camel_case_of_labelrf_labelinmatchgen_fieldvar_namejson_labelfield_typepkwith|None->()|Someexp->F.linepsc"let assoc = %s :: assoc in"expletgen_rft_optionalscrf_label(field_type,_,pk,_)=F.linepsc"let assoc = match v.%s with"rf_label;F.sub_scopesc(funsc->F.linesc"| None -> assoc";letjson_label=Pb_codegen_util.camel_case_of_labelrf_labelinmatchgen_field"v"json_labelfield_typepkwith|None->F.linesc"| Some v -> assoc"|Someexp->F.linepsc"| Some v -> %s :: assoc"exp);F.linesc"in"letgen_rft_repeatedscrf_labelrepeated_field=letrepeated_type,field_type,_,pk,_=repeated_fieldin(matchrepeated_typewith|Ot.Rt_list->()|Ot.Rt_repeated_field->sp"Pbrt.Repeated_field is not supported with JSON (field: %s)"rf_label|>failwith);letvar_name=sp"v.%s"rf_labelinletjson_label=Pb_codegen_util.camel_case_of_labelrf_labelinF.linesc"let assoc =";F.sub_scopesc(funsc->(matchfield_type,pkwith|Ot.Ft_unit,_->unsupportedjson_label|Ot.Ft_basic_typebasic_type,_->letruntime_f,map_function=runtime_function_for_basic_typejson_labelbasic_typepkin(matchmap_functionwith|None->F.linepsc"let l = %s |> List.map Pbrt_yojson.%s in"var_nameruntime_f|Somemap_function->F.linepsc"let l = %s |> List.map %s |> List.map Pbrt_yojson.%s in "var_namemap_functionruntime_f)(* TODO Wrapper: add similar case for Ft_wrapper_type *)(* User defined *)|Ot.Ft_user_defined_typeudt,_->letf_name=letfunction_prefix="encode_json"inPb_codegen_util.function_name_of_user_defined~function_prefixudtinF.linepsc"let l = %s |> List.map %s in"var_namef_name|_->unsupportedjson_label);F.linepsc"(\"%s\", `List l) :: assoc "json_label);F.linesc"in"letgen_rft_variantscrf_label{Ot.v_constructors;_}=F.linepsc"let assoc = match v.%s with"rf_label;F.sub_scopesc(funsc->List.iter(fun{Ot.vc_constructor;vc_field_type;vc_payload_kind;_}->letvar_name="v"inletjson_label=Pb_codegen_util.camel_case_of_constructorvc_constructorinF.sub_scopesc(funsc->matchvc_field_typewith|Ot.Vct_nullary->F.linepsc"| %s -> (\"%s\", `Null) :: assoc"vc_constructorjson_label|Ot.Vct_non_nullary_constructorfield_type->(matchgen_fieldvar_namejson_labelfield_typevc_payload_kindwith|None->F.linepsc"| %s -> (\"%s\", `Null) :: assoc"vc_constructorjson_label|Someexp->F.linepsc"| %s v -> %s :: assoc"vc_constructorexp)))v_constructors);F.linepsc"in (* match v.%s *)"rf_labelletgen_rft_assocsc~rf_label~assoc_type~key_type~value_field:(value_type,value_pk)=letvar_name=sp"v.%s"rf_labelinletjson_label=Pb_codegen_util.camel_case_of_labelrf_labelinletkey_pat,key_exp=matchkey_typewith|Ot.Bt_string->"key","key"|Ot.Bt_int->"key","(Int.to_string key)"|Ot.Bt_int32->"key","(Int32.to_string key)"|Ot.Bt_int64->"key","(Int64.to_string key)"|Ot.Bt_uint32->"(`unsigned key)","(Int32.to_string key)"|Ot.Bt_uint64->"(`unsigned key)","(Int64.to_string key)"|Ot.Bt_bool->"key","(Bool.to_string key)"|Ot.Bt_float->Printf.eprintf"float cannot be used as a map key type";exit1|Ot.Bt_bytes->Printf.eprintf"bytes cannot be used as a map key type";exit1inletwrite_assoc_field~fn~var_name=F.linesc"let assoc_field =";F.sub_scopesc(funsc->F.linepsc"%s"var_name;(matchassoc_typewith|Ot.At_list->()|Ot.At_hashtable->F.linesc"|> Hashtbl.to_seq |> List.of_seq");F.linepsc"|> List.map (fun (%s, value) -> %s, %s value)"key_patkey_expfn);F.linesc"in"inF.linesc"let assoc =";F.sub_scopesc(funsc->(matchvalue_typewith|Ot.Ft_unit->unsupportedjson_label|Ot.Ft_basic_typebasic_type->letruntime_f,map_function=runtime_function_for_basic_typejson_labelbasic_typevalue_pkin(matchmap_functionwith|None->write_assoc_field~fn:("Pbrt_yojson."^runtime_f)~var_name|Somemap_function->letfn=Printf.sprintf"(fun value -> value |> %s |> Pbrt_yojson.%s)"map_functionruntime_finwrite_assoc_field~fn~var_name)(* TODO Wrapper: add similar case for Ft_wrapper_type *)(* User defined *)|Ot.Ft_user_defined_typeudt->letfn=letfunction_prefix="encode_json"inPb_codegen_util.function_name_of_user_defined~function_prefixudtinwrite_assoc_field~fn~var_name|_->unsupportedjson_label);F.linepsc"(\"%s\", `Assoc assoc_field) :: assoc "json_label);F.linesc"in"letgen_record?and_{Ot.r_name;r_fields}sc=letrn=r_nameinF.linepsc"%s encode_json_%s (v:%s) = "(Pb_codegen_util.let_decl_of_andand_)rnrn;F.sub_scopesc(funsc->F.linesc"let assoc = [] in ";List.iter(funrecord_field->let{Ot.rf_label;rf_field_type;_}=record_fieldinmatchrf_field_typewith|Ot.Rft_nolabelnolabel_field->gen_rft_nolabelscrf_labelnolabel_field|Ot.Rft_repeatedrepeated_field->gen_rft_repeatedscrf_labelrepeated_field|Ot.Rft_variantvariant_field->gen_rft_variantscrf_labelvariant_field|Ot.Rft_optionaloptional_field->gen_rft_optionalscrf_labeloptional_field|Ot.Rft_required_->Printf.eprintf"Only proto3 syntax supported in JSON encoding";exit1|Ot.Rft_associative(assoc_type,_,(key_type,_),value_field)->gen_rft_assocsc~rf_label~assoc_type~key_type~value_field)r_fields(* List.iter *);F.linesc"`Assoc assoc")letgen_unit?and_{Ot.er_name}sc=letrn=er_nameinF.linepsc"%s encode_json_%s (v:%s) = "(Pb_codegen_util.let_decl_of_andand_)rnrn;F.linesc(sp"Pbrt_yojson.%s %s""make_unit""v")letgen_variant?and_{Ot.v_name;v_constructors}sc=letprocess_v_constructorscv_constructor=let{Ot.vc_constructor;Ot.vc_field_type;Ot.vc_payload_kind;_}=v_constructorinletjson_label=Pb_codegen_util.camel_case_of_constructorvc_constructorinmatchvc_field_typewith|Ot.Vct_nullary->F.linepsc"| %s -> `Assoc [(\"%s\", `Null)]"vc_constructorjson_label|Ot.Vct_non_nullary_constructorfield_type->(matchgen_field"v"json_labelfield_typevc_payload_kindwith|None->F.linepsc"| %s -> `Assoc [(\"%s\", `Null)]"vc_constructorjson_label|Someexp->F.linepsc"| %s v -> `Assoc [%s]"vc_constructorexp)inF.linepsc"%s encode_json_%s (v:%s) = "(Pb_codegen_util.let_decl_of_andand_)v_namev_name;F.sub_scopesc(funsc->F.linesc"begin match v with";List.iter(process_v_constructorsc)v_constructors;F.linesc"end")letgen_const_variant?and_{Ot.cv_name;Ot.cv_constructors}sc=F.linepsc"%s encode_json_%s (v:%s) = "(Pb_codegen_util.let_decl_of_andand_)cv_namecv_name;F.sub_scopesc(funsc->F.linesc"match v with";List.iter(fun{Ot.cvc_name;cvc_string_value;_}->F.linepsc"| %s -> `String \"%s\""cvc_namecvc_string_value)cv_constructors)letgen_struct?and_tsc=let{Ot.spec;_}=tinlethas_encoded=matchspecwith|Ot.Recordr->gen_record?and_rsc;true|Ot.Variantv->gen_variant?and_vsc;true|Ot.Const_variantv->gen_const_variant?and_vsc;true|Ot.Unitu->gen_unit?and_usc;trueinhas_encodedletgen_sig?and_tsc=let_=and_inletftype_name=F.linepsc"val encode_json_%s : %s -> Yojson.Basic.t"type_nametype_name;F.linepsc("(** [encode_json_%s v encoder] encodes [v] to "^^"to json *)")type_nameinmatchtwith|{Ot.spec=Ot.Record{Ot.r_name;_};_}->fr_name;true|{Ot.spec=Ot.Variantv;_}->fv.Ot.v_name;true|{Ot.spec=Ot.Const_variant{Ot.cv_name;_};_}->fcv_name;true|{Ot.spec=Ot.Unit{Ot.er_name;_};_}->fer_name;trueletocamldoc_title="Protobuf YoJson Encoding"letrequires_mutable_records=falseletplugin:Pb_codegen_plugin.t=letmoduleP=structletgen_sig=gen_sigletgen_struct=gen_structletocamldoc_title=ocamldoc_titleletrequires_mutable_records=requires_mutable_recordsendin(moduleP)