-module(libero@codegen). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/libero/codegen.gleam"). -export([module_to_underscored/1, to_pascal_case/1, module_to_mjs_path/2, endpoints_contain/2, variant_pattern/2, emit_client_msg_variants/2, collect_endpoint_type_modules/2, collect_endpoint_type_imports/3, is_dict/1, is_option/1, build_alias_resolver/1, import_if/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( " Cross-cutting helpers used by codegen submodules.\n" "\n" " Naming helpers and small predicates over `field_type.FieldType` graphs.\n" ). -file("src/libero/codegen.gleam", 16). ?DOC( " Convert a Gleam module path like \"shared/discount\" to a flat\n" " underscore-separated alias. e.g. \"shared/discount\" -> \"shared_discount\".\n" ). -spec module_to_underscored(binary()) -> binary(). module_to_underscored(Module_path) -> gleam@string:replace(Module_path, <<"/"/utf8>>, <<"_"/utf8>>). -file("src/libero/codegen.gleam", 21). ?DOC(" Convert a snake_case name to PascalCase.\n"). -spec to_pascal_case(binary()) -> binary(). to_pascal_case(Name) -> _pipe = Name, _pipe@1 = gleam@string:split(_pipe, <<"_"/utf8>>), _pipe@2 = gleam@list:map( _pipe@1, fun(Word) -> case gleam_stdlib:string_pop_grapheme(Word) of {ok, {First, Rest}} -> <<(string:uppercase(First))/binary, Rest/binary>>; {error, nil} -> Word end end ), gleam@string:join(_pipe@2, <<""/utf8>>). -file("src/libero/codegen.gleam", 36). ?DOC( " Convert a Gleam module path to its compiled .mjs bundle path.\n" " The package name is the Gleam package that owns the module (determines\n" " the top-level directory in the JS build output).\n" ). -spec module_to_mjs_path(binary(), binary()) -> binary(). module_to_mjs_path(Module_path, Package) -> <<<<<>/binary, Module_path/binary>>/binary, ".mjs"/utf8>>. -file("src/libero/codegen.gleam", 47). ?DOC( " True if any endpoint's parameter or return type (transitively)\n" " satisfies `predicate`.\n" ). -spec endpoints_contain( list(libero@scanner:handler_endpoint()), fun((libero@field_type:field_type()) -> boolean()) ) -> boolean(). endpoints_contain(Endpoints, Predicate) -> gleam@list:any( Endpoints, fun(E) -> (libero@field_type:contains(erlang:element(4, E), Predicate) orelse libero@field_type:contains( erlang:element(5, E), Predicate )) orelse gleam@list:any( erlang:element(6, E), fun(P) -> libero@field_type:contains(erlang:element(2, P), Predicate) end ) end ). -file("src/libero/codegen.gleam", 60). ?DOC( " Emit a constructor / pattern shape like `Variant(label1:, label2:)` or\n" " just `Variant` when there are no parameters.\n" ). -spec variant_pattern( binary(), list({binary(), libero@field_type:field_type()}) ) -> binary(). variant_pattern(Variant_name, Params) -> case Params of [] -> Variant_name; _ -> Labels = gleam@list:map( Params, fun(P) -> <<(erlang:element(1, P))/binary, ":"/utf8>> end ), <<<<<>/binary, (gleam@string:join(Labels, <<", "/utf8>>))/binary>>/binary, ")"/utf8>> end. -file("src/libero/codegen.gleam", 77). ?DOC( " Emit the body lines of the generated `ClientMsg` type. Uses\n" " `resolve_alias` to qualify user-defined types with the correct\n" " import alias (needed when multiple modules share the same last\n" " segment, e.g. two different `id_` modules).\n" ). -spec emit_client_msg_variants( list(libero@scanner:handler_endpoint()), fun((binary()) -> binary()) ) -> list(binary()). emit_client_msg_variants(Endpoints, Resolve_alias) -> gleam@list:map( Endpoints, fun(E) -> Variant_name = to_pascal_case( <<"server_"/utf8, (erlang:element(3, E))/binary>> ), case erlang:element(6, E) of [] -> <<" "/utf8, Variant_name/binary>>; Params -> Fields = gleam@list:map( Params, fun(P) -> {Label, Ft} = P, <<<