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([generate_decoders_gleam/1, emit_class_statics/1, generate_decoders_ffi/4, emit_typed_decoders/1]).
-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 JS decoder source from discovered types.\n"
" Returns strings; writing to disk is the consumer's responsibility.\n"
).
-file("src/libero/codegen_decoders.gleam", 19).
?DOC(" Generate the Gleam wrapper module source for the typed decoder FFI.\n").
-spec generate_decoders_gleam(binary()) -> binary().
generate_decoders_gleam(Ffi_module_path) ->
<<<<"//// 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, \"./"/utf8,
Ffi_module_path/binary>>/binary,
"\", \"ensure_decoders\")
@external(erlang, \"libero_etf_ffi\", \"ensure_decoders\")
pub fn ensure_decoders() -> Bool
"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 493).
-spec indent_js(binary()) -> binary().
indent_js(Source) ->
_pipe = Source,
_pipe@1 = gleam@string:trim(_pipe),
_pipe@2 = gleam@string:split(_pipe@1, <<"\n"/utf8>>),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(Line) -> <<" "/utf8, Line/binary>> end
),
_pipe@4 = gleam@string:join(_pipe@3, <<"\n"/utf8>>),
(fun(Source@1) -> <<Source@1/binary, "\n"/utf8>> end)(_pipe@4).
-file("src/libero/codegen_decoders.gleam", 479).
-spec emit_ensure_decoders(binary()) -> binary().
emit_ensure_decoders(Setup) ->
case gleam@string:trim(Setup) of
<<""/utf8>> ->
<<"export function ensure_decoders() { return true; }"/utf8>>;
_ ->
<<<<<<<<<<<<"let _decodersReady = false;\n\n"/utf8,
"export function ensure_decoders() {\n"/utf8>>/binary,
" if (_decodersReady) return true;\n"/utf8>>/binary,
(indent_js(Setup))/binary>>/binary,
" _decodersReady = true;\n"/utf8>>/binary,
" return true;\n"/utf8>>/binary,
"}"/utf8>>
end.
-file("src/libero/codegen_decoders.gleam", 217).
-spec variant_hash(libero@walker:discovered_variant()) -> binary().
variant_hash(V) ->
{_, Hash} = libero@wire_identity:wire_identity(
erlang:element(2, V),
erlang:element(3, V),
erlang:element(7, V)
),
Hash.
-file("src/libero/codegen_decoders.gleam", 267).
-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", 82).
-spec emit_typed_decoder_registrations(list(libero@walker:discovered_type())) -> binary().
emit_typed_decoder_registrations(Discovered) ->
case Discovered of
[] ->
<<""/utf8>>;
_ ->
Calls = begin
_pipe = gleam@list:flat_map(
Discovered,
fun(T) ->
Fn_name = decoder_fn_name(
erlang:element(2, T),
erlang:element(3, T)
),
gleam@list:map(
erlang:element(5, T),
fun(V) ->
<<<<<<<<<<<<"registerAtomDecoder(\""/utf8,
(variant_hash(V))/binary>>/binary,
"\", \""/utf8>>/binary,
Fn_name/binary>>/binary,
"\", "/utf8>>/binary,
Fn_name/binary>>/binary,
");"/utf8>>
end
)
end
),
gleam@string:join(_pipe, <<"\n"/utf8>>)
end,
<<Calls/binary, "\n"/utf8>>
end.
-file("src/libero/codegen_decoders.gleam", 231).
-spec emit_float_type_hint(libero@field_type:field_type()) -> binary().
emit_float_type_hint(Ft) ->
case Ft of
float_field ->
<<"\"float\""/utf8>>;
{list_of, Element} ->
<<<<"{ kind: \"list\", element: "/utf8,
(emit_float_type_hint(Element))/binary>>/binary,
" }"/utf8>>;
{option_of, Inner} ->
<<<<"{ kind: \"option\", inner: "/utf8,
(emit_float_type_hint(Inner))/binary>>/binary,
" }"/utf8>>;
{result_of, Ok, Err} ->
<<<<<<<<"{ kind: \"result\", ok: "/utf8,
(emit_float_type_hint(Ok))/binary>>/binary,
", err: "/utf8>>/binary,
(emit_float_type_hint(Err))/binary>>/binary,
" }"/utf8>>;
{dict_of, Key, Value} ->
<<<<<<<<"{ kind: \"dict\", key: "/utf8,
(emit_float_type_hint(Key))/binary>>/binary,
", value: "/utf8>>/binary,
(emit_float_type_hint(Value))/binary>>/binary,
" }"/utf8>>;
{tuple_of, Elements} ->
Hints = begin
_pipe = Elements,
_pipe@1 = gleam@list:map(_pipe, fun emit_float_type_hint/1),
gleam@string:join(_pipe@1, <<", "/utf8>>)
end,
<<<<"{ kind: \"tuple\", elements: ["/utf8, Hints/binary>>/binary,
"] }"/utf8>>;
int_field ->
<<"null"/utf8>>;
string_field ->
<<"null"/utf8>>;
bool_field ->
<<"null"/utf8>>;
bit_array_field ->
<<"null"/utf8>>;
nil_field ->
<<"null"/utf8>>;
{type_var, _} ->
<<"null"/utf8>>;
{user_type, _, _, _} ->
<<"null"/utf8>>
end.
-file("src/libero/codegen_decoders.gleam", 192).
?DOC(
" Emit per-variant class-static assignments for every discovered user\n"
" type. `__wireAtom` carries the 10-char hash; `__fieldTypes` carries\n"
" the positional field-type hints the encoder needs (Float marked,\n"
" containers wrapped, primitives null). These statics replace the\n"
" runtime `_bareToQualifiedAtom` and `fieldTypeRegistry` lookup tables.\n"
"\n"
" Even 0-arity variants get a `__fieldTypes = []` for symmetry; the\n"
" encoder branches on key count so the empty array is never indexed.\n"
).
-spec emit_class_statics(list(libero@walker:discovered_type())) -> binary().
emit_class_statics(Discovered) ->
Lines = gleam@list:flat_map(
Discovered,
fun(T) ->
gleam@list:flat_map(
erlang:element(5, T),
fun(V) ->
Class_ref = <<<<<<"_m_"/utf8,
(libero@codegen:module_to_underscored(
erlang:element(2, V)
))/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(3, V))/binary>>,
Hints = begin
_pipe = erlang:element(7, V),
_pipe@1 = gleam@list:map(
_pipe,
fun emit_float_type_hint/1
),
gleam@string:join(_pipe@1, <<", "/utf8>>)
end,
[<<<<<<Class_ref/binary, ".__wireAtom = \""/utf8>>/binary,
(variant_hash(V))/binary>>/binary,
"\";"/utf8>>,
<<<<<<Class_ref/binary, ".__fieldTypes = ["/utf8>>/binary,
Hints/binary>>/binary,
"];"/utf8>>]
end
)
end
),
case Lines of
[] ->
<<""/utf8>>;
_ ->
<<(gleam@string:join(Lines, <<"\n"/utf8>>))/binary, "\n"/utf8>>
end.
-file("src/libero/codegen_decoders.gleam", 296).
-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", 227).
-spec variant_source_label(libero@walker:discovered_variant()) -> binary().
variant_source_label(V) ->
<<<<(erlang:element(3, V))/binary, " @ "/utf8>>/binary,
(erlang:element(2, V))/binary>>.
-file("src/libero/codegen_decoders.gleam", 466).
-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", 456).
-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", 399).
-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", 394).
-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", 346).
-spec emit_tagged_union_decoder(list(libero@walker:discovered_variant())) -> binary().
emit_tagged_union_decoder(Variants) ->
Arms = gleam@list:map(
Variants,
fun(V) ->
Expected_len = erlang:integer_to_binary(
erlang:length(erlang:element(7, V)) + 1
),
Args = case erlang:element(7, 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,
Length_guard = case erlang:element(7, V) of
[] ->
<<<<<<<<<<<<"if (Array.isArray(term) && term.length !== "/utf8,
Expected_len/binary>>/binary,
") throw new DecodeError(\"expected "/utf8>>/binary,
(variant_hash(V))/binary>>/binary,
" arity "/utf8>>/binary,
Expected_len/binary>>/binary,
", got \" + term.length);\n"/utf8>>;
_ ->
<<<<<<<<<<<<"if (!Array.isArray(term) || term.length !== "/utf8,
Expected_len/binary>>/binary,
") throw new DecodeError(\"expected "/utf8>>/binary,
(variant_hash(V))/binary>>/binary,
" arity "/utf8>>/binary,
Expected_len/binary>>/binary,
", got \" + (Array.isArray(term) ? term.length : typeof term));\n"/utf8>>
end,
<<<<<<<<<<<<<<<<" case \""/utf8, (variant_hash(V))/binary>>/binary,
"\": // "/utf8>>/binary,
(variant_source_label(V))/binary>>/binary,
"\n "/utf8>>/binary,
Length_guard/binary>>/binary,
" 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 atom: \" + String(tag));\n"/utf8>>/binary,
" }"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 309).
-spec emit_enum_decoder(list(libero@walker:discovered_variant())) -> binary().
emit_enum_decoder(Variants) ->
Cases = gleam@list:map(
Variants,
fun(V) ->
<<<<<<<<<<" if (term === \""/utf8, (variant_hash(V))/binary>>/binary,
"\") return "/utf8>>/binary,
(emit_variant_constructor_call(V, <<""/utf8>>))/binary>>/binary,
"; // "/utf8>>/binary,
(variant_source_label(V))/binary>>
end
),
<<(gleam@string:join(Cases, <<"\n"/utf8>>))/binary,
"\n throw new DecodeError(\"unknown variant atom: \" + String(term));"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 274).
-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(7, V)) end
).
-file("src/libero/codegen_decoders.gleam", 323).
-spec emit_record_decoder(libero@walker:discovered_variant()) -> binary().
emit_record_decoder(Variant) ->
Field_lines = gleam@list:index_map(
erlang:element(7, 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>>,
Hash = variant_hash(Variant),
Label = variant_source_label(Variant),
Expected_len = erlang:integer_to_binary(
erlang:length(erlang:element(7, Variant)) + 1
),
<<<<<<<<<<<<<<<<<<<<<<" if (!Array.isArray(term) || term.length !== "/utf8,
Expected_len/binary>>/binary,
" || term[0] !== \""/utf8>>/binary,
Hash/binary>>/binary,
"\") throw new DecodeError(\"expected "/utf8>>/binary,
Hash/binary>>/binary,
" ("/utf8>>/binary,
Label/binary>>/binary,
"), got \" + (Array.isArray(term) ? String(term[0]) : typeof term));\n"/utf8>>/binary,
" return "/utf8>>/binary,
(emit_variant_constructor_call(Variant, Args))/binary>>/binary,
";"/utf8>>.
-file("src/libero/codegen_decoders.gleam", 278).
-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(7, 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", 76).
-spec emit_type_decoder_functions(list(libero@walker:discovered_type())) -> binary().
emit_type_decoder_functions(Discovered) ->
_pipe = Discovered,
_pipe@1 = gleam@list:map(_pipe, fun(T) -> emit_type_decoder(T) end),
gleam@string:join(_pipe@1, <<"\n\n"/utf8>>).
-file("src/libero/codegen_decoders.gleam", 169).
-spec module_owner(binary(), binary(), list(binary())) -> binary().
module_owner(Module_path, Package, Dependency_packages) ->
case gleam@string:split(Module_path, <<"/"/utf8>>) of
[First | _] ->
case gleam@list:contains(Dependency_packages, First) of
true ->
First;
false ->
Package
end;
_ ->
Package
end.
-file("src/libero/codegen_decoders.gleam", 107).
-spec emit_decoder_imports(
list(libero@walker:discovered_type()),
binary(),
binary(),
list(binary())
) -> binary().
emit_decoder_imports(Discovered, Relpath_prefix, Package, Dependency_packages) ->
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,
Relpath_prefix/binary>>/binary,
"libero/libero/decoders_prelude.mjs"/utf8>>/binary,
"\";"/utf8>>,
Stdlib_imports = <<<<<<<<<<<<<<<<"import { Ok, Error as ResultError, Empty, NonEmpty } from \""/utf8,
Relpath_prefix/binary>>/binary,
"gleam_stdlib/gleam.mjs\";\n"/utf8>>/binary,
"import { Some, None } from \""/utf8>>/binary,
Relpath_prefix/binary>>/binary,
"gleam_stdlib/gleam/option.mjs\";\n"/utf8>>/binary,
"import { from_list as dictFromList } from \""/utf8>>/binary,
Relpath_prefix/binary>>/binary,
"gleam_stdlib/gleam/dict.mjs\";"/utf8>>,
Decoder_ffi_import = case gleam@list:is_empty(Discovered) of
true ->
<<""/utf8>>;
false ->
<<<<"\nimport { registerAtomDecoder } from \""/utf8,
Relpath_prefix/binary>>/binary,
"libero/libero/etf/wire_ffi.mjs\";"/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,
Relpath_prefix/binary>>/binary,
(libero@codegen:module_to_mjs_path(
Mp,
module_owner(Mp, Package, Dependency_packages)
))/binary>>/binary,
"\";"/utf8>>
end
),
gleam@string:join(
[Prelude_import,
<<Stdlib_imports/binary, Decoder_ffi_import/binary>> |
Module_imports],
<<"\n"/utf8>>
).
-file("src/libero/codegen_decoders.gleam", 34).
?DOC(" Generate the JS FFI decoder source from discovered types.\n").
-spec generate_decoders_ffi(
list(libero@walker:discovered_type()),
binary(),
binary(),
list(binary())
) -> binary().
generate_decoders_ffi(Discovered, Relpath_prefix, Package, Dependency_packages) ->
Imports = emit_decoder_imports(
Discovered,
Relpath_prefix,
Package,
Dependency_packages
),
Body = emit_type_decoder_functions(Discovered),
Class_statics = emit_class_statics(Discovered),
Ctor_setters = <<<<<<"setResultCtors(Ok, ResultError);\n"/utf8,
"setOptionCtors(Some, None);\n"/utf8>>/binary,
"setListCtors(Empty, NonEmpty);\n"/utf8>>/binary,
"setDictFromList(dictFromList);\n"/utf8>>,
Registrations = emit_typed_decoder_registrations(Discovered),
Entry = emit_ensure_decoders(
<<<<Ctor_setters/binary, Class_statics/binary>>/binary,
Registrations/binary>>
),
<<<<<<<<<<"// Code generated by libero. DO NOT EDIT.
//
// Per-type decoder functions derived from the DiscoveredType graph.
"/utf8,
Imports/binary>>/binary,
"\n\n"/utf8>>/binary,
Body/binary>>/binary,
"\n\n"/utf8>>/binary,
Entry/binary>>.
-file("src/libero/codegen_decoders.gleam", 68).
?DOC(
" Emit a JS string with one decoder function per discovered type,\n"
" registration calls that populate etf/wire_ffi.mjs's typed decoder registry,\n"
" and an `ensure_decoders` entry point.\n"
).
-spec emit_typed_decoders(list(libero@walker:discovered_type())) -> binary().
emit_typed_decoders(Discovered) ->
Registrations = emit_typed_decoder_registrations(Discovered),
Entry = emit_ensure_decoders(Registrations),
Type_decoders = emit_type_decoder_functions(Discovered),
Parts = gleam@list:filter(
[Type_decoders, Entry],
fun(S) -> S /= <<""/utf8>> end
),
gleam@string:join(Parts, <<"\n\n"/utf8>>).