Current section

Files

Jump to
libero src libero@codegen_decoders.erl
Raw

src/libero@codegen_decoders.erl

-module(libero@codegen_decoders).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/libero/codegen_decoders.gleam").
-export([write_decoders_gleam/1, emit_typed_decoders/1, write_decoders_ffi/3]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Typed decoder generators.\n"
"\n"
" Emits the `rpc_decoders.gleam` Gleam wrapper and the per-type\n"
" `rpc_decoders_ffi.mjs` JavaScript module that the generated client\n"
" stubs use to decode ETF responses into typed Gleam values.\n"
).
-file("src/libero/codegen_decoders.gleam", 25).
?DOC(
" Write the Gleam wrapper for the typed decoder FFI.\n"
" Surfaces `ensure_decoders` from the generated JS FFI file to Gleam\n"
" callers, which triggers constructor and decoder registration on\n"
" import. JS-only, no Erlang target fallback.\n"
).
-spec write_decoders_gleam(libero@config:config()) -> {ok, nil} |
{error, libero@gen_error:gen_error()}.
write_decoders_gleam(Config) ->
Content = <<"//// Code generated by libero. DO NOT EDIT.
////
//// Gleam wrapper for the typed decoder FFI. Importing this module
//// triggers constructor and decoder registration on the JavaScript target.
@external(javascript, \"./rpc_decoders_ffi.mjs\", \"ensure_decoders\")
pub fn ensure_decoders() -> Bool
"/utf8>>,
Output = erlang:element(8, Config),
libero@codegen:ensure_parent_dir(Output),
libero@codegen:write_file(Output, Content).
-file("src/libero/codegen_decoders.gleam", 399).
?DOC(
" Emit the `ensure_decoders` FFI export. This is a no-op function whose\n"
" only purpose is to force the JS module to load, triggering the\n"
" constructor registration side effects that run at module scope.\n"
).
-spec emit_ensure_decoders() -> binary().
emit_ensure_decoders() ->
<<"export function ensure_decoders() { return true; }"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 237).
?DOC(
" Emit `new _m_<alias>.<Variant>(<args>)` for a discovered variant.\n"
" `args` is joined into the constructor call as-is; pass `\"\"` for a\n"
" zero-arg variant to get `new _m_alias.Variant()`.\n"
).
-spec emit_variant_constructor_call(
libero@walker:discovered_variant(),
binary()
) -> binary().
emit_variant_constructor_call(Variant, Args) ->
<<<<<<<<<<<<"new _m_"/utf8,
(libero@codegen:module_to_underscored(
erlang:element(2, Variant)
))/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(3, Variant))/binary>>/binary,
"("/utf8>>/binary,
Args/binary>>/binary,
")"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 203).
?DOC(
" Derive the JS decoder function name for a discovered type.\n"
" e.g. (\"shared/line_item\", \"Status\") -> \"decode_shared_line_item_status\"\n"
).
-spec decoder_fn_name(binary(), binary()) -> binary().
decoder_fn_name(Module_path, Type_name) ->
<<<<<<"decode_"/utf8,
(libero@codegen:module_to_underscored(Module_path))/binary>>/binary,
"_"/utf8>>/binary,
(libero@walker:to_snake_case(Type_name))/binary>>.
-file("src/libero/codegen_decoders.gleam", 383).
?DOC(
" Emit a comma-joined list of `(t<n>) => <decoder>` lambdas, one per\n"
" element of `inners`. The first lambda uses parameter `t<start_depth>`\n"
" and each subsequent lambda increments. The recursive decoder body\n"
" starts from `param_depth + 1` so it never reuses the lambda's param.\n"
).
-spec emit_lambda_decoders(list(libero@field_type:field_type()), integer()) -> binary().
emit_lambda_decoders(Inners, Start_depth) ->
_pipe = Inners,
_pipe@1 = gleam@list:index_map(
_pipe,
fun(Ft, I) ->
P_depth = Start_depth + I,
Param = <<"t"/utf8, (erlang:integer_to_binary(P_depth))/binary>>,
<<<<<<"("/utf8, Param/binary>>/binary, ") => "/utf8>>/binary,
(field_decoder_call_depth(Ft, Param, P_depth + 1))/binary>>
end
),
gleam@string:join(_pipe@1, <<", "/utf8>>).
-file("src/libero/codegen_decoders.gleam", 369).
?DOC(
" Emit `<fn_name>(<lambda1>, <lambda2>, ..., <term_expr>)` where each\n"
" lambda decodes one of `inners`. Used for the Result/Dict shape where\n"
" the wrapping JS function takes per-position decoders followed by the\n"
" term to decode. `start_depth` is the depth assigned to the first\n"
" lambda's parameter; subsequent lambdas advance by 1 each.\n"
).
-spec emit_higher_order_decoder(
binary(),
list(libero@field_type:field_type()),
binary(),
integer()
) -> binary().
emit_higher_order_decoder(Fn_name, Inners, Term_expr, Start_depth) ->
Decoders = emit_lambda_decoders(Inners, Start_depth),
<<<<<<<<<<Fn_name/binary, "("/utf8>>/binary, Decoders/binary>>/binary,
", "/utf8>>/binary,
Term_expr/binary>>/binary,
")"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 307).
-spec field_decoder_call_depth(
libero@field_type:field_type(),
binary(),
integer()
) -> binary().
field_decoder_call_depth(Ft, Term_expr, Depth) ->
Param = <<"t"/utf8, (erlang:integer_to_binary(Depth))/binary>>,
Next = Depth + 1,
case Ft of
int_field ->
<<<<"decode_int("/utf8, Term_expr/binary>>/binary, ")"/utf8>>;
float_field ->
<<<<"decode_float("/utf8, Term_expr/binary>>/binary, ")"/utf8>>;
string_field ->
<<<<"decode_string("/utf8, Term_expr/binary>>/binary, ")"/utf8>>;
bool_field ->
<<<<"decode_bool("/utf8, Term_expr/binary>>/binary, ")"/utf8>>;
bit_array_field ->
<<<<"decode_bit_array("/utf8, Term_expr/binary>>/binary, ")"/utf8>>;
nil_field ->
<<<<"decode_nil("/utf8, Term_expr/binary>>/binary, ")"/utf8>>;
{list_of, Inner} ->
<<<<<<<<<<<<"decode_list_of(("/utf8, Param/binary>>/binary,
") => "/utf8>>/binary,
(field_decoder_call_depth(Inner, Param, Next))/binary>>/binary,
", "/utf8>>/binary,
Term_expr/binary>>/binary,
")"/utf8>>;
{option_of, Inner@1} ->
<<<<<<<<<<<<"decode_option_of(("/utf8, Param/binary>>/binary,
") => "/utf8>>/binary,
(field_decoder_call_depth(Inner@1, Param, Next))/binary>>/binary,
", "/utf8>>/binary,
Term_expr/binary>>/binary,
")"/utf8>>;
{result_of, Ok, Err} ->
emit_higher_order_decoder(
<<"decode_result_of"/utf8>>,
[Ok, Err],
Term_expr,
Next
);
{dict_of, K, V} ->
emit_higher_order_decoder(
<<"decode_dict_of"/utf8>>,
[K, V],
Term_expr,
Next
);
{tuple_of, Elems} ->
Decoders = emit_lambda_decoders(Elems, Next),
<<<<<<<<"decode_tuple_of(["/utf8, Decoders/binary>>/binary,
"], "/utf8>>/binary,
Term_expr/binary>>/binary,
")"/utf8>>;
{type_var, Name} ->
<<<<"(() => { throw new DecodeError(\"TypeVar<"/utf8, Name/binary>>/binary,
"> not supported at runtime\"); })()"/utf8>>;
{user_type, Module_path, Type_name, _} ->
<<<<<<(decoder_fn_name(Module_path, Type_name))/binary, "("/utf8>>/binary,
Term_expr/binary>>/binary,
")"/utf8>>
end.
-file("src/libero/codegen_decoders.gleam", 302).
?DOC(
" Produce the JS expression that decodes `term_expr` according to `ft`.\n"
" `depth` tracks nesting to generate unique lambda param names (t0, t1, ...).\n"
).
-spec field_decoder_call(libero@field_type:field_type(), binary()) -> binary().
field_decoder_call(Ft, Term_expr) ->
field_decoder_call_depth(Ft, Term_expr, 0).
-file("src/libero/codegen_decoders.gleam", 275).
?DOC(" Emit the body of a multi-variant tagged union decoder.\n").
-spec emit_tagged_union_decoder(list(libero@walker:discovered_variant())) -> binary().
emit_tagged_union_decoder(Variants) ->
Arms = gleam@list:map(
Variants,
fun(V) ->
Args = case erlang:element(6, V) of
[] ->
<<""/utf8>>;
Fields ->
_pipe = Fields,
_pipe@1 = gleam@list:index_map(
_pipe,
fun(Ft, I) ->
field_decoder_call(
Ft,
<<<<"term["/utf8,
(erlang:integer_to_binary(I + 1))/binary>>/binary,
"]"/utf8>>
)
end
),
gleam@string:join(_pipe@1, <<", "/utf8>>)
end,
<<<<<<<<" case \""/utf8, (erlang:element(4, V))/binary>>/binary,
"\":\n return "/utf8>>/binary,
(emit_variant_constructor_call(V, Args))/binary>>/binary,
";"/utf8>>
end
),
<<<<<<<<" const tag = Array.isArray(term) ? term[0] : term;\n"/utf8,
" switch (tag) {\n"/utf8>>/binary,
(gleam@string:join(Arms, <<"\n"/utf8>>))/binary>>/binary,
"\n default:\n throw new DecodeError(\"unknown variant: \" + String(tag));\n"/utf8>>/binary,
" }"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 251).
?DOC(" Emit the body of a pure-enum decoder (no-field variants only).\n").
-spec emit_enum_decoder(list(libero@walker:discovered_variant())) -> binary().
emit_enum_decoder(Variants) ->
Cases = gleam@list:map(
Variants,
fun(V) ->
<<<<<<<<" if (term === \""/utf8, (erlang:element(4, V))/binary>>/binary,
"\") return "/utf8>>/binary,
(emit_variant_constructor_call(V, <<""/utf8>>))/binary>>/binary,
";"/utf8>>
end
),
<<(gleam@string:join(Cases, <<"\n"/utf8>>))/binary,
"\n throw new DecodeError(\"unknown variant: \" + String(term));"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 211).
?DOC(" True when every variant in the list has zero fields (pure enum).\n").
-spec all_variants_zero_arity(list(libero@walker:discovered_variant())) -> boolean().
all_variants_zero_arity(Variants) ->
gleam@list:all(
Variants,
fun(V) -> gleam@list:is_empty(erlang:element(6, V)) end
).
-file("src/libero/codegen_decoders.gleam", 265).
?DOC(" Emit the body of a single-variant record decoder.\n").
-spec emit_record_decoder(libero@walker:discovered_variant()) -> binary().
emit_record_decoder(Variant) ->
Field_lines = gleam@list:index_map(
erlang:element(6, Variant),
fun(Ft, I) ->
<<" "/utf8,
(field_decoder_call(
Ft,
<<<<"term["/utf8, (erlang:integer_to_binary(I + 1))/binary>>/binary,
"]"/utf8>>
))/binary>>
end
),
Args = <<<<"\n"/utf8,
(gleam@string:join(Field_lines, <<",\n"/utf8>>))/binary>>/binary,
"\n "/utf8>>,
<<<<" return "/utf8,
(emit_variant_constructor_call(Variant, Args))/binary>>/binary,
";"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 216).
?DOC(" Emit one JS decoder function for a discovered type.\n").
-spec emit_type_decoder(libero@walker:discovered_type()) -> binary().
emit_type_decoder(T) ->
Fn_name = decoder_fn_name(erlang:element(2, T), erlang:element(3, T)),
Body = case erlang:element(5, T) of
[] ->
<<" throw new DecodeError(\"empty type\");"/utf8>>;
[Single] ->
case erlang:element(6, Single) of
[] ->
emit_enum_decoder(erlang:element(5, T));
_ ->
emit_record_decoder(Single)
end;
Variants ->
case all_variants_zero_arity(Variants) of
true ->
emit_enum_decoder(Variants);
false ->
emit_tagged_union_decoder(Variants)
end
end,
<<<<<<<<"export function "/utf8, Fn_name/binary>>/binary,
"(term) {\n"/utf8>>/binary,
Body/binary>>/binary,
"\n}"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 43).
?DOC(
" Emit a JS string with one decoder function per discovered type and an\n"
" `ensure_decoders` entry point. Does not write to disk; exposed so\n"
" tests can assert on the output without filesystem I/O.\n"
).
-spec emit_typed_decoders(list(libero@walker:discovered_type())) -> binary().
emit_typed_decoders(Discovered) ->
Type_decoders = gleam@list:map(
Discovered,
fun(T) -> emit_type_decoder(T) end
),
Entry = emit_ensure_decoders(),
Parts = gleam@list:filter(
[gleam@string:join(Type_decoders, <<"\n\n"/utf8>>), Entry],
fun(S) -> S /= <<""/utf8>> end
),
gleam@string:join(Parts, <<"\n\n"/utf8>>).
-file("src/libero/codegen_decoders.gleam", 173).
-spec emit_float_field_registrations(list(libero@walker:discovered_type())) -> binary().
emit_float_field_registrations(Discovered) ->
Lines = gleam@list:flat_map(
Discovered,
fun(T) ->
gleam@list:filter_map(
erlang:element(5, T),
fun(V) -> case erlang:element(5, V) of
[] ->
{error, nil};
Indices ->
Index_list = begin
_pipe = Indices,
_pipe@1 = gleam@list:map(
_pipe,
fun erlang:integer_to_binary/1
),
gleam@string:join(_pipe@1, <<", "/utf8>>)
end,
{ok,
<<<<<<<<"registerFloatFields(\""/utf8,
(erlang:element(4, V))/binary>>/binary,
"\", ["/utf8>>/binary,
Index_list/binary>>/binary,
"]);"/utf8>>}
end end
)
end
),
case Lines of
[] ->
<<""/utf8>>;
_ ->
<<(gleam@string:join(Lines, <<"\n"/utf8>>))/binary, "\n"/utf8>>
end.
-file("src/libero/codegen_decoders.gleam", 425).
?DOC(
" Emit the shared response-shape decoder. Maps the wire shape\n"
" `Result(Result(payload, domain), RpcError)` into an\n"
" `RpcData(payload, domain)` value. Per-endpoint decoders just supply\n"
" the two payload-level decoders, removing ~14 lines of boilerplate\n"
" each from the generated FFI.\n"
).
-spec emit_response_helper() -> binary().
emit_response_helper() ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<"function _decode_response(raw, decode_ok, decode_err) {\n"/utf8,
" if (Array.isArray(raw)) {\n"/utf8>>/binary,
" if (raw[0] === \"ok\") {\n"/utf8>>/binary,
" const inner = raw[1];\n"/utf8>>/binary,
" if (Array.isArray(inner) && inner[0] === \"ok\") {\n"/utf8>>/binary,
" return new _Success(decode_ok(inner[1]));\n"/utf8>>/binary,
" } else if (Array.isArray(inner) && inner[0] === \"error\") {\n"/utf8>>/binary,
" return new _Failure(new _DomainError(decode_err(inner[1])));\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" } else if (raw[0] === \"error\") {\n"/utf8>>/binary,
" return new _Failure(new _TransportError(_decode_rpc_error(raw[1])));\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" return new _Failure(new _TransportError(new _MalformedRequest()));\n"/utf8>>/binary,
"}"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 408).
?DOC(
" Emit a hand-written decoder for `RpcError`. The shape is fixed\n"
" (libero/error.gleam: `MalformedRequest | UnknownFunction(name) |\n"
" InternalError(trace_id, message)`) so it isn't discovered through\n"
" the type walker. Generated once, called by every per-endpoint\n"
" response decoder for the outer-Error path.\n"
).
-spec emit_rpc_error_decoder() -> binary().
emit_rpc_error_decoder() ->
<<<<<<<<<<<<<<"function _decode_rpc_error(term) {\n"/utf8,
" if (term === \"malformed_request\") return new _MalformedRequest();\n"/utf8>>/binary,
" if (Array.isArray(term)) {\n"/utf8>>/binary,
" if (term[0] === \"unknown_function\") return new _UnknownFunction(decode_string(term[1]));\n"/utf8>>/binary,
" if (term[0] === \"internal_error\") return new _InternalError(decode_string(term[1]), decode_string(term[2]));\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" return new _MalformedRequest();\n"/utf8>>/binary,
"}"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 447).
?DOC(
" Emit per-endpoint response decoder functions for the FFI file.\n"
" Each decoder is a thin wrapper around `_decode_response`, supplying the\n"
" payload and domain-error decoders for that endpoint's `Result` return.\n"
).
-spec emit_response_decoders(list(libero@scanner:handler_endpoint())) -> binary().
emit_response_decoders(Endpoints) ->
case Endpoints of
[] ->
<<""/utf8>>;
_ ->
Decoders = gleam@list:map(
Endpoints,
fun(E) ->
Ok_decoder = field_decoder_call(
erlang:element(4, E),
<<"t"/utf8>>
),
Err_decoder = field_decoder_call(
erlang:element(5, E),
<<"t"/utf8>>
),
<<<<<<<<<<<<<<<<"export function decode_response_"/utf8,
(erlang:element(3, E))/binary>>/binary,
"(raw) {\n"/utf8>>/binary,
" return _decode_response(raw, (t) => "/utf8>>/binary,
Ok_decoder/binary>>/binary,
", (t) => "/utf8>>/binary,
Err_decoder/binary>>/binary,
");\n"/utf8>>/binary,
"}"/utf8>>
end
),
<<<<<<<<<<<<"\n// --- Per-endpoint response decoders ---\n\n"/utf8,
(emit_rpc_error_decoder())/binary>>/binary,
"\n\n"/utf8>>/binary,
(emit_response_helper())/binary>>/binary,
"\n\n"/utf8>>/binary,
(gleam@string:join(Decoders, <<"\n\n"/utf8>>))/binary>>/binary,
"\n"/utf8>>
end.
-file("src/libero/codegen_decoders.gleam", 167).
-spec discovered_has_float_fields(list(libero@walker:discovered_type())) -> boolean().
discovered_has_float_fields(Discovered) ->
gleam@list:any(
Discovered,
fun(T) ->
gleam@list:any(
erlang:element(5, T),
fun(V) -> not gleam@list:is_empty(erlang:element(5, V)) end
)
end
).
-file("src/libero/codegen_decoders.gleam", 85).
?DOC(
" Emit a JS import block for the typed decoders file. The endpoint\n"
" list determines whether the per-endpoint response decoders need the\n"
" RemoteData constructors imported.\n"
).
-spec emit_decoder_imports(
list(libero@walker:discovered_type()),
libero@config:config(),
list(libero@scanner:handler_endpoint())
) -> binary().
emit_decoder_imports(Discovered, Config, Endpoints) ->
Module_paths = begin
_pipe = gleam@list:fold(
Discovered,
{[], gleam@set:new()},
fun(Acc, T) ->
{Paths_acc, Seen} = Acc,
case gleam@set:contains(Seen, erlang:element(2, T)) of
true ->
Acc;
false ->
{lists:append(Paths_acc, [erlang:element(2, T)]),
gleam@set:insert(Seen, erlang:element(2, T))}
end
end
),
(fun(Pair) -> erlang:element(1, Pair) end)(_pipe)
end,
Prelude_import = <<<<<<<<<<<<"import { decode_int, decode_float, decode_string, decode_bool, "/utf8,
"decode_bit_array, decode_nil, decode_list_of, decode_option_of, "/utf8>>/binary,
"decode_result_of, decode_dict_of, decode_tuple_of, DecodeError, "/utf8>>/binary,
"setResultCtors, setOptionCtors, setListCtors, "/utf8>>/binary,
"setDictFromList } from \""/utf8>>/binary,
(erlang:element(9, Config))/binary>>/binary,
"\";"/utf8>>,
Prefix = erlang:element(6, Config),
Stdlib_imports = <<<<<<<<<<<<<<<<"import { Ok, Error as ResultError, Empty, NonEmpty } from \""/utf8,
Prefix/binary>>/binary,
"gleam_stdlib/gleam.mjs\";\n"/utf8>>/binary,
"import { Some, None } from \""/utf8>>/binary,
Prefix/binary>>/binary,
"gleam_stdlib/gleam/option.mjs\";\n"/utf8>>/binary,
"import { from_list as dictFromList } from \""/utf8>>/binary,
Prefix/binary>>/binary,
"gleam_stdlib/gleam/dict.mjs\";"/utf8>>,
Float_import = case discovered_has_float_fields(Discovered) of
true ->
<<<<"\nimport { registerFloatFields } from \""/utf8, Prefix/binary>>/binary,
"libero/libero/rpc_ffi.mjs\";"/utf8>>;
false ->
<<""/utf8>>
end,
Module_imports = gleam@list:map(
Module_paths,
fun(Mp) ->
<<<<<<<<<<"import * as _m_"/utf8,
(libero@codegen:module_to_underscored(Mp))/binary>>/binary,
" from \""/utf8>>/binary,
(erlang:element(6, Config))/binary>>/binary,
(libero@codegen:module_to_mjs_path(Mp))/binary>>/binary,
"\";"/utf8>>
end
),
Remote_data_import = case Endpoints of
[] ->
<<""/utf8>>;
_ ->
<<<<<<"\nimport { Success as _Success, Failure as _Failure, "/utf8,
"TransportError as _TransportError, DomainError as _DomainError } from \""/utf8>>/binary,
Prefix/binary>>/binary,
"libero/libero/remote_data.mjs\";"/utf8>>
end,
Rpc_error_import = case Endpoints of
[] ->
<<""/utf8>>;
_ ->
<<<<<<<<"\nimport { MalformedRequest as _MalformedRequest, "/utf8,
"UnknownFunction as _UnknownFunction, "/utf8>>/binary,
"InternalError as _InternalError } from \""/utf8>>/binary,
Prefix/binary>>/binary,
"libero/libero/error.mjs\";"/utf8>>
end,
gleam@string:join(
[Prelude_import,
<<<<<<Stdlib_imports/binary, Float_import/binary>>/binary,
Remote_data_import/binary>>/binary,
Rpc_error_import/binary>> |
Module_imports],
<<"\n"/utf8>>
).
-file("src/libero/codegen_decoders.gleam", 53).
?DOC(
" Write the typed decoders FFI file for the consumer. Per-endpoint\n"
" response decoders are appended after the per-type decoders.\n"
).
-spec write_decoders_ffi(
libero@config:config(),
list(libero@walker:discovered_type()),
list(libero@scanner:handler_endpoint())
) -> {ok, nil} | {error, libero@gen_error:gen_error()}.
write_decoders_ffi(Config, Discovered, Endpoints) ->
Imports = emit_decoder_imports(Discovered, Config, Endpoints),
Body = emit_typed_decoders(Discovered),
Response_decoders = emit_response_decoders(Endpoints),
Float_field_registrations = emit_float_field_registrations(Discovered),
Ctor_setters = <<<<<<"setResultCtors(Ok, ResultError);\n"/utf8,
"setOptionCtors(Some, None);\n"/utf8>>/binary,
"setListCtors(Empty, NonEmpty);\n"/utf8>>/binary,
"setDictFromList(dictFromList);\n"/utf8>>,
Content = <<<<<<<<<<<<<<<<"// Code generated by libero. DO NOT EDIT.
//
// Per-type decoder functions derived from the DiscoveredType graph.
// Eliminates the global constructor registry - each decoder knows
// exactly which module's constructor to instantiate.
"/utf8,
Imports/binary>>/binary,
"\n\n"/utf8>>/binary,
Ctor_setters/binary>>/binary,
Float_field_registrations/binary>>/binary,
"\n"/utf8>>/binary,
Body/binary>>/binary,
"\n"/utf8>>/binary,
Response_decoders/binary>>,
Output = erlang:element(7, Config),
libero@codegen:ensure_parent_dir(Output),
libero@codegen:write_file(Output, Content).