src/rally@generator.erl
-module(rally@generator).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/rally/generator.gleam").
-export([generate/1, generate_dispatch/5, generate_empty_rpc_dispatch/2, normalize_rpc_dispatch_context_import/1, normalize_rpc_dispatch_unused_fields/1, generate_protocol_wire/7, generate_protocol_wire_js/2]).
-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(
" Route type, parse function, path builder, and page dispatch codegen.\n"
"\n"
" Generates router.gleam (Route type, parse_route, route_to_path, href),\n"
" page_dispatch.gleam (PageModel/PageMsg unions, per-route init/update/view),\n"
" protocol_wire.gleam facade, and RPC dispatch scaffolding.\n"
).
-file("src/rally/generator.gleam", 373).
-spec generate_href() -> binary().
generate_href() ->
<<"pub fn href(route route: Route) -> Attribute(msg) {\n attribute.href(route_to_path(route: route))\n}"/utf8>>.
-file("src/rally/generator.gleam", 333).
-spec merge_static_segments(
list(rally@types:url_segment()),
binary(),
list(binary())
) -> list(binary()).
merge_static_segments(Segments, Buf, Acc) ->
case Segments of
[] ->
case Buf of
<<""/utf8>> ->
lists:reverse(Acc);
_ ->
lists:reverse(
[<<<<"\""/utf8, Buf/binary>>/binary, "\""/utf8>> | Acc]
)
end;
[{static_segment, Name} | Rest] ->
merge_static_segments(
Rest,
<<<<Buf/binary, "/"/utf8>>/binary, Name/binary>>,
Acc
);
[{dynamic_segment, Param_name, int_param} | Rest@1] ->
Acc@1 = case Buf of
<<""/utf8>> ->
[<<"\"/\""/utf8>> | Acc];
_ ->
[<<<<"\""/utf8, Buf/binary>>/binary, "/\""/utf8>> | Acc]
end,
merge_static_segments(
Rest@1,
<<""/utf8>>,
[<<<<"int.to_string("/utf8, Param_name/binary>>/binary,
")"/utf8>> |
Acc@1]
);
[{dynamic_segment, Param_name@1, _} | Rest@2] ->
Acc@2 = case Buf of
<<""/utf8>> ->
[<<"\"/\""/utf8>> | Acc];
_ ->
[<<<<"\""/utf8, Buf/binary>>/binary, "/\""/utf8>> | Acc]
end,
merge_static_segments(
Rest@2,
<<""/utf8>>,
[<<<<"uri.percent_encode("/utf8, Param_name@1/binary>>/binary,
")"/utf8>> |
Acc@2]
)
end.
-file("src/rally/generator.gleam", 322).
-spec build_path_expr(list(rally@types:url_segment())) -> binary().
build_path_expr(Segments) ->
case Segments of
[] ->
<<"\"/\""/utf8>>;
_ ->
gleam@string:join(
merge_static_segments(Segments, <<""/utf8>>, []),
<<" <> "/utf8>>
)
end.
-file("src/rally/generator.gleam", 240).
?DOC(" Build the constructor expression using param names directly (shorthand label syntax).\n").
-spec build_constructor(binary(), list({binary(), rally@types:param_type()})) -> binary().
build_constructor(Variant_name, Params) ->
case Params of
[] ->
Variant_name;
_ ->
Fields = begin
_pipe = gleam@list:map(
Params,
fun(P) ->
{Name, _} = P,
<<Name/binary, ":"/utf8>>
end
),
gleam@string:join(_pipe, <<", "/utf8>>)
end,
<<<<<<Variant_name/binary, "("/utf8>>/binary, Fields/binary>>/binary,
")"/utf8>>
end.
-file("src/rally/generator.gleam", 309).
-spec route_to_path_arm(rally@types:scanned_route()) -> binary().
route_to_path_arm(Route) ->
{scanned_route, Segments, Variant_name, Params, _, _} = Route,
Destructor = build_constructor(Variant_name, Params),
Path_expr = build_path_expr(Segments),
<<<<<<" "/utf8, Destructor/binary>>/binary, " -> "/utf8>>/binary,
Path_expr/binary>>.
-file("src/rally/generator.gleam", 299).
-spec generate_route_to_path(list(rally@types:scanned_route())) -> binary().
generate_route_to_path(Routes) ->
Arms = begin
_pipe = Routes,
_pipe@1 = gleam@list:map(_pipe, fun route_to_path_arm/1),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
<<<<"pub fn route_to_path(route route: Route) -> String {\n case route {\n"/utf8,
Arms/binary>>/binary,
"\n NotFound(uri:) -> uri.to_string(uri)\n }\n}"/utf8>>.
-file("src/rally/generator.gleam", 259).
?DOC(" Build the constructor expression substituting int params with their _val names.\n").
-spec build_constructor_with_vals(
binary(),
list({binary(), rally@types:param_type()}),
list({binary(), binary()})
) -> binary().
build_constructor_with_vals(Variant_name, Params, Val_pairs) ->
case Params of
[] ->
Variant_name;
_ ->
Fields = begin
_pipe = gleam@list:map(
Params,
fun(P) ->
{Name, Param_type} = P,
case Param_type of
string_param ->
<<<<<<<<<<Name/binary,
": result.unwrap(uri.percent_decode("/utf8>>/binary,
Name/binary>>/binary,
"), "/utf8>>/binary,
Name/binary>>/binary,
")"/utf8>>;
int_param ->
Val_name = case gleam@list:find(
Val_pairs,
fun(Vp) ->
erlang:element(1, Vp) =:= Name
end
) of
{ok, {_, V}} ->
V;
{error, nil} ->
Name
end,
<<<<Name/binary, ": "/utf8>>/binary,
Val_name/binary>>
end
end
),
gleam@string:join(_pipe, <<", "/utf8>>)
end,
<<<<<<Variant_name/binary, "("/utf8>>/binary, Fields/binary>>/binary,
")"/utf8>>
end.
-file("src/rally/generator.gleam", 227).
?DOC(" Build the list pattern for a route's segments, prefixed with [_lang, \"admin\"].\n").
-spec build_pattern(list(rally@types:url_segment())) -> binary().
build_pattern(Segments) ->
Seg_parts = gleam@list:map(Segments, fun(Seg) -> case Seg of
{static_segment, Name} ->
<<<<"\""/utf8, Name/binary>>/binary, "\""/utf8>>;
{dynamic_segment, Param_name, _} ->
Param_name
end end),
All_parts = Seg_parts,
<<<<"["/utf8, (gleam@string:join(All_parts, <<", "/utf8>>))/binary>>/binary,
"]"/utf8>>.
-file("src/rally/generator.gleam", 141).
-spec parse_route_arm(rally@types:scanned_route()) -> binary().
parse_route_arm(Route) ->
{scanned_route, Segments, Variant_name, Params, _, _} = Route,
Pattern = build_pattern(Segments),
Int_params = gleam@list:filter(
Params,
fun(P) ->
{_, Pt} = P,
Pt =:= int_param
end
),
case Int_params of
[] ->
String_params = gleam@list:filter(
Params,
fun(P@1) ->
{_, Pt@1} = P@1,
Pt@1 =:= string_param
end
),
Constructor = case String_params of
[] ->
build_constructor(Variant_name, Params);
_ ->
build_constructor_with_vals(Variant_name, Params, [])
end,
<<<<<<" "/utf8, Pattern/binary>>/binary, " -> "/utf8>>/binary,
Constructor/binary>>;
[Single] ->
{Name, _} = Single,
Val_name = <<Name/binary, "_val"/utf8>>,
Constructor@1 = build_constructor_with_vals(
Variant_name,
Params,
[{Name, Val_name}]
),
<<<<<<<<<<<<<<<<" "/utf8, Pattern/binary>>/binary,
" ->\n case int.parse("/utf8>>/binary,
Name/binary>>/binary,
") {\n Ok("/utf8>>/binary,
Val_name/binary>>/binary,
") -> "/utf8>>/binary,
Constructor@1/binary>>/binary,
"\n Error(_) -> NotFound(uri:)\n }"/utf8>>;
Multiple ->
Parse_exprs = begin
_pipe = gleam@list:map(
Multiple,
fun(P@2) ->
{Name@1, _} = P@2,
<<<<"int.parse("/utf8, Name@1/binary>>/binary,
")"/utf8>>
end
),
gleam@string:join(_pipe, <<", "/utf8>>)
end,
Ok_patterns = begin
_pipe@1 = gleam@list:map(
Multiple,
fun(P@3) ->
{Name@2, _} = P@3,
<<<<"Ok("/utf8, Name@2/binary>>/binary, "_val)"/utf8>>
end
),
gleam@string:join(_pipe@1, <<", "/utf8>>)
end,
Val_pairs = gleam@list:map(
Multiple,
fun(P@4) ->
{Name@3, _} = P@4,
{Name@3, <<Name@3/binary, "_val"/utf8>>}
end
),
Constructor@2 = build_constructor_with_vals(
Variant_name,
Params,
Val_pairs
),
<<<<<<<<<<<<<<<<<<<<" "/utf8, Pattern/binary>>/binary,
" ->\n case "/utf8>>/binary,
Parse_exprs/binary>>/binary,
" {\n "/utf8>>/binary,
Ok_patterns/binary>>/binary,
" -> "/utf8>>/binary,
Constructor@2/binary>>/binary,
"\n "/utf8>>/binary,
(gleam@string:join(
gleam@list:map(Multiple, fun(_) -> <<"_"/utf8>> end),
<<", "/utf8>>
))/binary>>/binary,
" -> NotFound(uri:)\n }"/utf8>>
end.
-file("src/rally/generator.gleam", 131).
-spec generate_parse_route(list(rally@types:scanned_route())) -> binary().
generate_parse_route(Routes) ->
Arms = begin
_pipe = Routes,
_pipe@1 = gleam@list:map(_pipe, fun parse_route_arm/1),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
<<<<"pub fn parse_route(uri: Uri) -> Route {\n case uri.path_segments(uri.path) {\n"/utf8,
Arms/binary>>/binary,
"\n _ -> NotFound(uri:)\n }\n}"/utf8>>.
-file("src/rally/generator.gleam", 120).
-spec param_type_to_gleam(rally@types:param_type()) -> binary().
param_type_to_gleam(Param_type) ->
case Param_type of
int_param ->
<<"Int"/utf8>>;
string_param ->
<<"String"/utf8>>
end.
-file("src/rally/generator.gleam", 105).
-spec route_variant_line(rally@types:scanned_route()) -> binary().
route_variant_line(Route) ->
case erlang:element(4, Route) of
[] ->
<<" "/utf8, (erlang:element(3, Route))/binary>>;
Params ->
Fields = begin
_pipe = gleam@list:map(
Params,
fun(P) ->
{Name, Param_type} = P,
<<<<Name/binary, ": "/utf8>>/binary,
(param_type_to_gleam(Param_type))/binary>>
end
),
gleam@string:join(_pipe, <<", "/utf8>>)
end,
<<<<<<<<" "/utf8, (erlang:element(3, Route))/binary>>/binary,
"("/utf8>>/binary,
Fields/binary>>/binary,
")"/utf8>>
end.
-file("src/rally/generator.gleam", 96).
-spec generate_route_type(list(rally@types:scanned_route())) -> binary().
generate_route_type(Routes) ->
Variants = begin
_pipe = Routes,
_pipe@1 = gleam@list:map(_pipe, fun route_variant_line/1),
_pipe@2 = lists:append(_pipe@1, [<<" NotFound(uri: Uri)"/utf8>>]),
gleam@string:join(_pipe@2, <<"\n"/utf8>>)
end,
<<<<"pub type Route {\n"/utf8, Variants/binary>>/binary, "\n}"/utf8>>.
-file("src/rally/generator.gleam", 88).
-spec has_string_params(list(rally@types:scanned_route())) -> boolean().
has_string_params(Routes) ->
gleam@list:any(
Routes,
fun(R) ->
gleam@list:any(
erlang:element(4, R),
fun(P) -> erlang:element(2, P) =:= string_param end
)
end
).
-file("src/rally/generator.gleam", 84).
-spec has_int_params(list(rally@types:scanned_route())) -> boolean().
has_int_params(Routes) ->
gleam@list:any(
Routes,
fun(R) ->
gleam@list:any(
erlang:element(4, R),
fun(P) -> erlang:element(2, P) =:= int_param end
)
end
).
-file("src/rally/generator.gleam", 67).
-spec file_header(list(rally@types:scanned_route())) -> binary().
file_header(Routes) ->
Int_import = case has_int_params(Routes) of
true ->
<<"import gleam/int\n"/utf8>>;
false ->
<<""/utf8>>
end,
Result_import = case has_string_params(Routes) of
true ->
<<"import gleam/result\n"/utf8>>;
false ->
<<""/utf8>>
end,
Uri_import = <<"import gleam/uri.{type Uri}\n"/utf8>>,
<<<<<<<<"// Generated by Rally — do not edit.\n\n"/utf8, Int_import/binary>>/binary,
Result_import/binary>>/binary,
Uri_import/binary>>/binary,
"import lustre/attribute.{type Attribute}"/utf8>>.
-file("src/rally/generator.gleam", 54).
-spec compare_segments(rally@types:url_segment(), rally@types:url_segment()) -> gleam@order:order().
compare_segments(A, B) ->
case {A, B} of
{{static_segment, _}, {dynamic_segment, _, _}} ->
lt;
{{dynamic_segment, _, _}, {static_segment, _}} ->
gt;
{{static_segment, An}, {static_segment, Bn}} ->
gleam@string:compare(An, Bn);
{{dynamic_segment, An@1, _}, {dynamic_segment, Bn@1, _}} ->
gleam@string:compare(An@1, Bn@1)
end.
-file("src/rally/generator.gleam", 41).
-spec compare_routes(
list(rally@types:url_segment()),
list(rally@types:url_segment())
) -> gleam@order:order().
compare_routes(A, B) ->
case {A, B} of
{[], []} ->
eq;
{[], _} ->
lt;
{_, []} ->
gt;
{[Ah | At], [Bh | Bt]} ->
case compare_segments(Ah, Bh) of
eq ->
compare_routes(At, Bt);
Other ->
Other
end
end.
-file("src/rally/generator.gleam", 37).
?DOC(
" Sort routes so that static patterns come before dynamic patterns at each\n"
" level of nesting. Within static groups, sort alphabetically.\n"
).
-spec sort_routes(list(rally@types:scanned_route())) -> list(rally@types:scanned_route()).
sort_routes(Routes) ->
gleam@list:sort(
Routes,
fun(A, B) ->
compare_routes(erlang:element(2, A), erlang:element(2, B))
end
).
-file("src/rally/generator.gleam", 21).
?DOC(" Generate a complete Gleam source file from a list of scanned routes.\n").
-spec generate(list(rally@types:scanned_route())) -> binary().
generate(Routes) ->
Sorted = sort_routes(Routes),
Header = file_header(Routes),
Route_type = generate_route_type(Sorted),
Parse_fn = generate_parse_route(Sorted),
Path_fn = generate_route_to_path(Sorted),
Href_fn = generate_href(),
<<(gleam@string:join(
[Header, Route_type, Parse_fn, Path_fn, Href_fn],
<<"\n\n"/utf8>>
))/binary,
"\n"/utf8>>.
-file("src/rally/generator.gleam", 679).
-spec page_module_alias(rally@types:scanned_route()) -> binary().
page_module_alias(Route) ->
gleam@string:replace(erlang:element(5, Route), <<"/"/utf8>>, <<"_"/utf8>>).
-file("src/rally/generator.gleam", 616).
-spec dispatch_view_page(
list(rally@types:scanned_route()),
gleam@dict:dict(binary(), rally@types:page_contract()),
boolean()
) -> binary().
dispatch_view_page(Routes, Contract_map, Has_client_context) ->
Arms = begin
_pipe = Routes,
_pipe@1 = gleam@list:filter_map(
_pipe,
fun(Route) ->
case gleam_stdlib:map_get(
Contract_map,
erlang:element(3, Route)
) of
{ok, Contract} when erlang:element(7, Contract) ->
Alias = page_module_alias(Route),
View_args = case Has_client_context of
true ->
<<"(client_context, model)"/utf8>>;
false ->
<<"(model)"/utf8>>
end,
{ok,
<<<<<<<<<<<<<<<<<<" "/utf8,
(erlang:element(
3,
Route
))/binary>>/binary,
"PageModel(model) ->\n"/utf8>>/binary,
" element.map("/utf8>>/binary,
Alias/binary>>/binary,
".view"/utf8>>/binary,
View_args/binary>>/binary,
", "/utf8>>/binary,
(erlang:element(3, Route))/binary>>/binary,
"PageMsg)"/utf8>>};
_ ->
{error, nil}
end
end
),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
Signature = case Has_client_context of
true ->
<<"pub fn view_page(page_model page_model: PageModel, client_context client_context: client_context.ClientContext) -> Element(PageMsg) {"/utf8>>;
false ->
<<"pub fn view_page(page_model: PageModel) -> Element(PageMsg) {"/utf8>>
end,
<<<<<<Signature/binary, "\n case page_model {\n"/utf8>>/binary,
Arms/binary>>/binary,
"\n NoPageModel -> html.div([], [html.text(\"Page not found\")])\n }\n}"/utf8>>.
-file("src/rally/generator.gleam", 529).
-spec dispatch_update_page(
list(rally@types:scanned_route()),
gleam@dict:dict(binary(), rally@types:page_contract()),
boolean()
) -> binary().
dispatch_update_page(Routes, Contract_map, Has_client_context) ->
Arms = begin
_pipe = Routes,
_pipe@1 = gleam@list:filter_map(
_pipe,
fun(Route) ->
case gleam_stdlib:map_get(
Contract_map,
erlang:element(3, Route)
) of
{ok, Contract} when erlang:element(7, Contract) ->
Alias = page_module_alias(Route),
Vn = erlang:element(3, Route),
case {Has_client_context, erlang:element(8, Contract)} of
{true, true} ->
{ok,
<<<<<<<<<<<<<<<<<<<<<<<<<<" "/utf8,
Vn/binary>>/binary,
"PageModel(model), "/utf8>>/binary,
Vn/binary>>/binary,
"PageMsg(msg) -> {\n"/utf8>>/binary,
" let #(new_model, effects, context_msg) = "/utf8>>/binary,
Alias/binary>>/binary,
".update(client_context, model, msg)\n"/utf8>>/binary,
" #("/utf8>>/binary,
Vn/binary>>/binary,
"PageModel(new_model), effect.map(effects, "/utf8>>/binary,
Vn/binary>>/binary,
"PageMsg), context_msg)\n"/utf8>>/binary,
" }"/utf8>>};
{true, false} ->
{ok,
<<<<<<<<<<<<<<<<<<<<<<<<<<" "/utf8,
Vn/binary>>/binary,
"PageModel(model), "/utf8>>/binary,
Vn/binary>>/binary,
"PageMsg(msg) -> {\n"/utf8>>/binary,
" let #(new_model, effects) = "/utf8>>/binary,
Alias/binary>>/binary,
".update(client_context, model, msg)\n"/utf8>>/binary,
" #("/utf8>>/binary,
Vn/binary>>/binary,
"PageModel(new_model), effect.map(effects, "/utf8>>/binary,
Vn/binary>>/binary,
"PageMsg), None)\n"/utf8>>/binary,
" }"/utf8>>};
{_, _} ->
{ok,
<<<<<<<<<<<<<<<<<<<<<<<<<<" "/utf8,
Vn/binary>>/binary,
"PageModel(model), "/utf8>>/binary,
Vn/binary>>/binary,
"PageMsg(msg) -> {\n"/utf8>>/binary,
" let #(new_model, effects) = "/utf8>>/binary,
Alias/binary>>/binary,
".update(model, msg)\n"/utf8>>/binary,
" #("/utf8>>/binary,
Vn/binary>>/binary,
"PageModel(new_model), effect.map(effects, "/utf8>>/binary,
Vn/binary>>/binary,
"PageMsg))\n"/utf8>>/binary,
" }"/utf8>>}
end;
_ ->
{error, nil}
end
end
),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
case Has_client_context of
true ->
<<<<<<"pub fn update_page(page_model page_model: PageModel, page_msg page_msg: PageMsg, client_context client_context: client_context.ClientContext) -> #(PageModel, Effect(PageMsg), Option(client_context.ClientContextMsg)) {\n"/utf8,
" case page_model, page_msg {\n"/utf8>>/binary,
Arms/binary>>/binary,
"\n _, _ -> #(page_model, effect.none(), None)\n }\n}"/utf8>>;
false ->
<<<<<<"pub fn update_page(page_model page_model: PageModel, page_msg page_msg: PageMsg) -> #(PageModel, Effect(PageMsg)) {\n"/utf8,
" case page_model, page_msg {\n"/utf8>>/binary,
Arms/binary>>/binary,
"\n _, _ -> #(page_model, effect.none())\n }\n}"/utf8>>
end.
-file("src/rally/generator.gleam", 661).
-spec dispatch_route_pattern(rally@types:scanned_route()) -> binary().
dispatch_route_pattern(Route) ->
case erlang:element(4, Route) of
[] ->
<<"router."/utf8, (erlang:element(3, Route))/binary>>;
Params ->
<<<<<<<<"router."/utf8, (erlang:element(3, Route))/binary>>/binary,
"("/utf8>>/binary,
(gleam@string:join(
gleam@list:map(
Params,
fun(P) -> erlang:element(1, P) end
),
<<", "/utf8>>
))/binary>>/binary,
")"/utf8>>
end.
-file("src/rally/generator.gleam", 673).
-spec dispatch_route_param_args(rally@types:scanned_route()) -> binary().
dispatch_route_param_args(Route) ->
_pipe = erlang:element(4, Route),
_pipe@1 = gleam@list:map(
_pipe,
fun(P) -> <<", "/utf8, (erlang:element(1, P))/binary>> end
),
gleam@string:join(_pipe@1, <<""/utf8>>).
-file("src/rally/generator.gleam", 477).
-spec dispatch_init_page(
list(rally@types:scanned_route()),
gleam@dict:dict(binary(), rally@types:page_contract()),
boolean()
) -> binary().
dispatch_init_page(Routes, Contract_map, Has_client_context) ->
Arms = begin
_pipe = Routes,
_pipe@1 = gleam@list:filter_map(
_pipe,
fun(Route) ->
case gleam_stdlib:map_get(
Contract_map,
erlang:element(3, Route)
) of
{ok, Contract} when erlang:element(7, Contract) andalso erlang:element(
5,
Contract
) ->
Alias = page_module_alias(Route),
Param_args = dispatch_route_param_args(Route),
Call_args = case {Has_client_context, Param_args} of
{true, _} ->
<<<<"(client_context"/utf8, Param_args/binary>>/binary,
")"/utf8>>;
{false, <<""/utf8>>} ->
<<"()"/utf8>>;
{false, _} ->
<<<<"("/utf8,
(gleam@string:drop_start(Param_args, 2))/binary>>/binary,
")"/utf8>>
end,
{ok,
<<<<<<<<<<<<<<<<<<<<<<<<<<" "/utf8,
(dispatch_route_pattern(
Route
))/binary>>/binary,
" -> {\n"/utf8>>/binary,
" let #(model, effects) = "/utf8>>/binary,
Alias/binary>>/binary,
".init"/utf8>>/binary,
Call_args/binary>>/binary,
"\n"/utf8>>/binary,
" #("/utf8>>/binary,
(erlang:element(3, Route))/binary>>/binary,
"PageModel(model), effect.map(effects, "/utf8>>/binary,
(erlang:element(3, Route))/binary>>/binary,
"PageMsg))\n"/utf8>>/binary,
" }"/utf8>>};
_ ->
{error, nil}
end
end
),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
Signature = case Has_client_context of
true ->
<<"pub fn init_page(route route: router.Route, client_context client_context: client_context.ClientContext) -> #(PageModel, Effect(PageMsg)) {"/utf8>>;
false ->
<<"pub fn init_page(route: router.Route) -> #(PageModel, Effect(PageMsg)) {"/utf8>>
end,
<<<<<<Signature/binary, "\n case route {\n"/utf8>>/binary, Arms/binary>>/binary,
"\n _ -> #(NoPageModel, effect.none())\n }\n}"/utf8>>.
-file("src/rally/generator.gleam", 458).
-spec dispatch_page_msg_type(
list(rally@types:scanned_route()),
gleam@dict:dict(binary(), rally@types:page_contract())
) -> binary().
dispatch_page_msg_type(Routes, Contract_map) ->
Variants = begin
_pipe = Routes,
_pipe@1 = gleam@list:filter_map(
_pipe,
fun(Route) ->
case gleam_stdlib:map_get(
Contract_map,
erlang:element(3, Route)
) of
{ok, Contract} when erlang:element(7, Contract) ->
Alias = page_module_alias(Route),
{ok,
<<<<<<<<" "/utf8,
(erlang:element(3, Route))/binary>>/binary,
"PageMsg("/utf8>>/binary,
Alias/binary>>/binary,
".Msg)"/utf8>>};
_ ->
{error, nil}
end
end
),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
<<<<"pub type PageMsg {\n"/utf8, Variants/binary>>/binary,
"\n NoPageMsg\n}"/utf8>>.
-file("src/rally/generator.gleam", 439).
-spec dispatch_page_model_type(
list(rally@types:scanned_route()),
gleam@dict:dict(binary(), rally@types:page_contract())
) -> binary().
dispatch_page_model_type(Routes, Contract_map) ->
Variants = begin
_pipe = Routes,
_pipe@1 = gleam@list:filter_map(
_pipe,
fun(Route) ->
case gleam_stdlib:map_get(
Contract_map,
erlang:element(3, Route)
) of
{ok, Contract} when erlang:element(7, Contract) ->
Alias = page_module_alias(Route),
{ok,
<<<<<<<<" "/utf8,
(erlang:element(3, Route))/binary>>/binary,
"PageModel("/utf8>>/binary,
Alias/binary>>/binary,
".Model)"/utf8>>};
_ ->
{error, nil}
end
end
),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
<<<<"pub type PageModel {\n"/utf8, Variants/binary>>/binary,
"\n NoPageModel\n}"/utf8>>.
-file("src/rally/generator.gleam", 432).
-spec last_segment(binary()) -> binary().
last_segment(Module_path) ->
case begin
_pipe = gleam@string:split(Module_path, <<"/"/utf8>>),
gleam@list:last(_pipe)
end of
{ok, Seg} ->
Seg;
{error, nil} ->
Module_path
end.
-file("src/rally/generator.gleam", 425).
-spec import_as(binary(), binary()) -> binary().
import_as(Module_path, Alias) ->
case last_segment(Module_path) =:= Alias of
true ->
<<"import "/utf8, Module_path/binary>>;
false ->
<<<<<<"import "/utf8, Module_path/binary>>/binary, " as "/utf8>>/binary,
Alias/binary>>
end.
-file("src/rally/generator.gleam", 683).
-spec dispatch_page_imports(
list(rally@types:scanned_route()),
gleam@dict:dict(binary(), rally@types:page_contract())
) -> binary().
dispatch_page_imports(Routes, Contract_map) ->
_pipe = Routes,
_pipe@1 = gleam@list:filter_map(
_pipe,
fun(Route) ->
case gleam_stdlib:map_get(Contract_map, erlang:element(3, Route)) of
{ok, Contract} when erlang:element(7, Contract) ->
{ok,
<<<<<<"import "/utf8,
(erlang:element(5, Route))/binary>>/binary,
" as "/utf8>>/binary,
(page_module_alias(Route))/binary>>};
_ ->
{error, nil}
end
end
),
_pipe@2 = gleam@list:unique(_pipe@1),
_pipe@3 = gleam@list:sort(_pipe@2, fun gleam@string:compare/2),
gleam@string:join(_pipe@3, <<"\n"/utf8>>).
-file("src/rally/generator.gleam", 382).
?DOC(" Generate a complete page_dispatch.gleam source file from scanned routes.\n").
-spec generate_dispatch(
list(rally@types:scanned_route()),
list({rally@types:scanned_route(), rally@types:page_contract()}),
boolean(),
binary(),
binary()
) -> binary().
generate_dispatch(
Routes,
Contracts,
Has_client_context,
Router_module,
Client_context_module
) ->
Contract_map = begin
_pipe = Contracts,
_pipe@1 = gleam@list:map(
_pipe,
fun(Pair) ->
{erlang:element(3, (erlang:element(1, Pair))),
erlang:element(2, Pair)}
end
),
maps:from_list(_pipe@1)
end,
Page_imports = dispatch_page_imports(Routes, Contract_map),
Ctx_import = case Has_client_context of
true ->
<<"\n"/utf8,
(import_as(Client_context_module, <<"client_context"/utf8>>))/binary>>;
false ->
<<""/utf8>>
end,
Option_import = case Has_client_context of
true ->
<<"import gleam/option.{type Option, None}\n"/utf8>>;
false ->
<<""/utf8>>
end,
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"// Generated by Rally — do not edit.\n\n"/utf8,
(import_as(
Router_module,
<<"router"/utf8>>
))/binary>>/binary,
"\n"/utf8>>/binary,
Option_import/binary>>/binary,
"import lustre/effect.{type Effect}
import lustre/element.{type Element}
import lustre/element/html
"/utf8>>/binary,
Page_imports/binary>>/binary,
Ctx_import/binary>>/binary,
"\n\n"/utf8>>/binary,
(dispatch_page_model_type(
Routes,
Contract_map
))/binary>>/binary,
"\n\n"/utf8>>/binary,
(dispatch_page_msg_type(Routes, Contract_map))/binary>>/binary,
"\n\n"/utf8>>/binary,
(dispatch_init_page(
Routes,
Contract_map,
Has_client_context
))/binary>>/binary,
"\n\n"/utf8>>/binary,
(dispatch_update_page(Routes, Contract_map, Has_client_context))/binary>>/binary,
"\n\n"/utf8>>/binary,
(dispatch_view_page(Routes, Contract_map, Has_client_context))/binary>>.
-file("src/rally/generator.gleam", 700).
-spec generate_empty_rpc_dispatch(
binary(),
list(libero@codegen_dispatch:extra_param())
) -> binary().
generate_empty_rpc_dispatch(Atoms_module, Extra_params) ->
Extra_imports = begin
_pipe = gleam@list:filter_map(
Extra_params,
fun(P) -> case erlang:element(4, P) of
<<""/utf8>> ->
{error, nil};
Line ->
{ok, Line}
end end
),
_pipe@1 = gleam@string:join(_pipe, <<"\n"/utf8>>),
(fun(S) -> case S of
<<""/utf8>> ->
<<""/utf8>>;
_ ->
<<S/binary, "\n"/utf8>>
end end)(_pipe@1)
end,
Extra_handle_params = begin
_pipe@2 = gleam@list:map(
Extra_params,
fun(P@1) ->
<<<<<<<<<<<<"\n "/utf8, (erlang:element(2, P@1))/binary>>/binary,
" _"/utf8>>/binary,
(erlang:element(2, P@1))/binary>>/binary,
": "/utf8>>/binary,
(erlang:element(3, P@1))/binary>>/binary,
","/utf8>>
end
),
gleam@string:join(_pipe@2, <<""/utf8>>)
end,
<<<<<<<<<<<<"//// Code generated by libero. DO NOT EDIT.
import libero/error.{MalformedRequest, UnknownFunction}
import libero/wire
import server_context.{type ServerContext}
"/utf8,
Extra_imports/binary>>/binary,
"
@external(erlang, \""/utf8>>/binary,
Atoms_module/binary>>/binary,
"\", \"ensure\")
fn ensure_atoms() -> Nil
pub type ClientMsg {
NoClientMessages
}
pub fn handle(
server_context server_context: ServerContext,
data data: BitArray,"/utf8>>/binary,
Extra_handle_params/binary>>/binary,
"
) -> #(BitArray, ServerContext) {
ensure_atoms()
case wire.decode_request(data) {
Ok(#(\"rpc\", request_id, _msg)) ->
#(wire.encode_response(request_id:, value: Error(UnknownFunction(\"rpc\"))), server_context)
Ok(#(name, request_id, _)) ->
#(wire.encode_response(request_id:, value: Error(UnknownFunction(name))), server_context)
Error(_) ->
#(wire.encode_response(request_id: 0, value: Error(MalformedRequest)), server_context)
}
}
"/utf8>>.
-file("src/rally/generator.gleam", 755).
-spec normalize_rpc_dispatch_context_import(binary()) -> binary().
normalize_rpc_dispatch_context_import(Source) ->
gleam@string:replace(
Source,
<<"import server/server_context.{type ServerContext}"/utf8>>,
<<"import server_context.{type ServerContext}"/utf8>>
).
-file("src/rally/generator.gleam", 803).
-spec underscore_field_shorthand(binary()) -> binary().
underscore_field_shorthand(Field) ->
Trimmed = gleam@string:trim(Field),
case gleam_stdlib:string_ends_with(Trimmed, <<":"/utf8>>) of
true ->
<<Field/binary, " _"/utf8>>;
false ->
Field
end.
-file("src/rally/generator.gleam", 770).
-spec underscore_unused_dispatch_fields(binary()) -> binary().
underscore_unused_dispatch_fields(Line) ->
case gleam@string:split_once(Line, <<" -> {"/utf8>>) of
{ok, {Before_arrow, After_arrow}} ->
case gleam@string:split_once(Before_arrow, <<"("/utf8>>) of
{ok, {Before_open, After_open}} ->
case gleam@string:split_once(After_open, <<")"/utf8>>) of
{ok, {Fields, <<""/utf8>>}} ->
case gleam_stdlib:contains_string(
Fields,
<<":"/utf8>>
) of
true ->
Underscored_fields = begin
_pipe = Fields,
_pipe@1 = gleam@string:split(
_pipe,
<<","/utf8>>
),
_pipe@2 = gleam@list:map(
_pipe@1,
fun underscore_field_shorthand/1
),
gleam@string:join(_pipe@2, <<","/utf8>>)
end,
<<<<<<<<Before_open/binary, "("/utf8>>/binary,
Underscored_fields/binary>>/binary,
") -> {"/utf8>>/binary,
After_arrow/binary>>;
false ->
Line
end;
_ ->
Line
end;
_ ->
Line
end;
_ ->
Line
end.
-file("src/rally/generator.gleam", 763).
-spec normalize_rpc_dispatch_unused_fields(binary()) -> binary().
normalize_rpc_dispatch_unused_fields(Source) ->
_pipe = Source,
_pipe@1 = gleam@string:split(_pipe, <<"\n"/utf8>>),
_pipe@2 = gleam@list:map(_pipe@1, fun underscore_unused_dispatch_fields/1),
gleam@string:join(_pipe@2, <<"\n"/utf8>>).
-file("src/rally/generator.gleam", 973).
-spec etf_protocol_wire_source(
binary(),
binary(),
gleam@option:option(rally@types:auth_config())
) -> binary().
etf_protocol_wire_source(Atoms_module, Rpc_dispatch_module, Auth_config) ->
Has_auth = gleam@option:is_some(Auth_config),
Auth_import = case Auth_config of
{some, {auth_config, Auth_module}} ->
<<(import_as(Auth_module, <<"auth"/utf8>>))/binary, "\n"/utf8>>;
none ->
<<""/utf8>>
end,
Dispatch_fn = case Has_auth of
true ->
<<"pub fn dispatch_rpc(
envelope envelope: RpcEnvelope,
server_context server_context: ServerContext,
identity identity: auth.Identity,
) -> #(RpcResult, ServerContext) {
let #(data, server_context) =
rpc_dispatch.handle(server_context:, data: envelope.raw, identity:)
#(RpcResult(data:), server_context)
}
"/utf8>>;
false ->
<<"pub fn dispatch_rpc(
envelope envelope: RpcEnvelope,
server_context server_context: ServerContext,
) -> #(RpcResult, ServerContext) {
let #(data, server_context) =
rpc_dispatch.handle(server_context:, data: envelope.raw)
#(RpcResult(data:), server_context)
}
"/utf8>>
end,
<<<<<<<<<<<<<<<<"// Generated by Rally — do not edit.
import gleam/bytes_tree
import gleam/dynamic.{type Dynamic}
import libero/error.{type DecodeError}
import libero/wire as libero_wire
import mist.{type WebsocketConnection, type WebsocketMessage}
import server_context.{type ServerContext}
import "/utf8,
Rpc_dispatch_module/binary>>/binary,
" as rpc_dispatch
"/utf8>>/binary,
Auth_import/binary>>/binary,
"
@external(erlang, \""/utf8>>/binary,
Atoms_module/binary>>/binary,
"\", \"ensure\")
fn ensure_atoms() -> Nil
pub fn page_init_ok() -> Nil { Nil }
pub fn encode(value: a) -> BitArray { libero_wire.encode(value) }
pub fn decode_request(data: BitArray) -> Result(#(String, Int, Dynamic), DecodeError) { libero_wire.decode_request(data) }
pub fn encode_request(module module: String, request_id request_id: Int, msg msg: a) -> BitArray { libero_wire.encode_request(module:, request_id:, msg:) }
pub fn encode_response(request_id request_id: Int, value value: a) -> BitArray { libero_wire.encode_response(request_id:, value:) }
pub fn tag_response(request_id request_id: Int, data data: BitArray) -> BitArray { libero_wire.tag_response(request_id:, data:) }
pub fn encode_push(module module: String, value value: a) -> BitArray { libero_wire.encode_push(module:, value:) }
pub fn variant_tag(value: Dynamic) -> Result(String, Nil) { libero_wire.variant_tag(value) }
pub fn coerce(value: a) -> b { libero_wire.coerce(value) }
pub fn encode_flags(value: a) -> String { libero_wire.encode_flags(value) }
pub fn decode_flags_typed(flags: String, decoder_name: String) -> Result(a, DecodeError) { libero_wire.decode_flags_typed(flags:, decoder_name:) }
pub opaque type RpcEnvelope {
RpcEnvelope(request_id: Int, identity: String, raw: BitArray)
}
pub opaque type RpcResult {
RpcResult(data: BitArray)
}
pub fn rpc_request_id(envelope: RpcEnvelope) -> Int { envelope.request_id }
pub fn rpc_identity(envelope: RpcEnvelope) -> String { envelope.identity }
pub fn rpc_raw_payload(envelope: RpcEnvelope) -> BitArray { envelope.raw }
pub fn decode_rpc_envelope(data: BitArray) -> Result(RpcEnvelope, Nil) {
case libero_wire.decode_request(data) {
Ok(#(_module, request_id, raw)) ->
case libero_wire.variant_tag(raw) {
Ok(tag) -> Ok(RpcEnvelope(request_id:, identity: tag, raw: data))
Error(Nil) -> Error(Nil)
}
Error(_) -> Error(Nil)
}
}
pub fn decode_ws_rpc_envelope(msg: WebsocketMessage(a)) -> Result(RpcEnvelope, Nil) {
case msg {
mist.Binary(data) ->
case decode_rpc_envelope(data) {
Ok(envelope) if envelope.request_id == 0 -> Error(Nil)
result -> result
}
_ -> Error(Nil)
}
}
"/utf8>>/binary,
Dispatch_fn/binary>>/binary,
"
pub fn send_rpc_result(conn: WebsocketConnection, result: RpcResult) -> Nil {
let _send_result = mist.send_binary_frame(conn, result.data)
Nil
}
pub fn rpc_result_body(result: RpcResult) -> bytes_tree.BytesTree {
bytes_tree.from_bit_array(result.data)
}
pub fn rpc_content_type() -> String {
\"application/octet-stream\"
}
pub fn auth_error_result(request_id: Int, message: String) -> RpcResult {
RpcResult(data: encode_response(request_id:, value: Error(message)))
}
pub fn error_result(request_id: Int, message: String) -> RpcResult {
RpcResult(data: encode_response(request_id:, value: Error(message)))
}
pub fn malformed_rpc_result() -> RpcResult {
RpcResult(data: encode_response(request_id: 0, value: Error(\"Bad request\")))
}
@external(erlang, \"rally_runtime_wire_ffi\", \"tuple_element\")
pub fn tuple_element(_tuple: Dynamic, _index: Int) -> Dynamic { dynamic.nil() }
"/utf8>>.
-file("src/rally/generator.gleam", 1100).
-spec json_protocol_wire_source(
binary(),
binary(),
list(libero@scanner:handler_endpoint()),
gleam@option:option(rally@types:auth_config()),
binary()
) -> binary().
json_protocol_wire_source(
Contract_hash,
_,
Endpoints,
Auth_config,
Wire_import_module
) ->
Has_auth = gleam@option:is_some(Auth_config),
Auth_import = case Auth_config of
{some, {auth_config, Auth_module}} ->
<<(import_as(Auth_module, <<"auth"/utf8>>))/binary, "\n"/utf8>>;
none ->
<<""/utf8>>
end,
Json_codecs_module = gleam@string:replace(
Wire_import_module,
<<"protocol_wire"/utf8>>,
<<"json_codecs"/utf8>>
),
Handler_imports = case rally@generator@json_rpc_dispatch:handler_imports(
Endpoints
) of
[] ->
<<""/utf8>>;
Imports ->
<<(gleam@string:join(Imports, <<"\n"/utf8>>))/binary, "\n"/utf8>>
end,
Json_dispatch = rally@generator@json_rpc_dispatch:generate_json_dispatch_function_with_prefix(
Endpoints,
Has_auth,
<<""/utf8>>
),
Dispatch_fn = case Has_auth of
true ->
<<"pub fn dispatch_rpc(
envelope envelope: RpcEnvelope,
server_context server_context: ServerContext,
identity identity: auth.Identity,
) -> #(RpcResult, ServerContext) {
let #(frame, server_context) =
json_dispatch(
message: envelope.message,
request_id: envelope.request_id,
server_context:,
identity:,
)
#(RpcResult(text: frame), server_context)
}
"/utf8>>;
false ->
<<"pub fn dispatch_rpc(
envelope envelope: RpcEnvelope,
server_context server_context: ServerContext,
) -> #(RpcResult, ServerContext) {
let #(frame, server_context) =
json_dispatch(
message: envelope.message,
request_id: envelope.request_id,
server_context:,
)
#(RpcResult(text: frame), server_context)
}
"/utf8>>
end,
<<<<<<<<<<<<<<<<<<<<<<"// Generated by Rally — do not edit.
import gleam/bit_array
import gleam/bytes_tree
import gleam/dynamic.{type Dynamic}
import gleam/dynamic/decode
import gleam/io
import gleam/json
import gleam/option.{type Option, None, Some}
import libero/frame.{type ServerFrame}
import libero/json/error.{type JsonError, JsonError}
import libero/json/wire.{type RequestEnvelope} as json_wire
import libero/trace
import mist.{type WebsocketConnection, type WebsocketMessage}
import server_context.{type ServerContext}
import "/utf8,
Json_codecs_module/binary>>/binary,
" as json_codecs
"/utf8>>/binary,
Handler_imports/binary>>/binary,
Auth_import/binary>>/binary,
"
const contract_hash = \""/utf8>>/binary,
Contract_hash/binary>>/binary,
"\"
pub fn page_init_ok() -> json.Json { json.null() }
pub fn encode_request(module module: String, request_id request_id: Int, msg msg: json.Json) -> String {
json_wire.encode_request(module:, request_id:, msg:, contract_hash:)
}
pub fn decode_server_frame(data: String) -> Result(ServerFrame(Dynamic), List(JsonError)) {
json_wire.decode_server_frame(data)
}
pub fn encode_response(request_id request_id: Int, value value: json.Json) -> String {
json_wire.encode_response(request_id:, value:)
}
pub fn encode_error(request_id request_id: Option(Int), errors errors: List(JsonError)) -> String {
json_wire.encode_error(request_id:, errors:)
}
pub fn encode_push(module module: String, value value: json.Json) -> String {
json_wire.encode_push(module:, value:)
}
pub fn encode_flags(value: json.Json) -> String {
json_wire.encode_flags(value)
}
pub fn decode_flags_typed(flags: String, decoder: fn(Dynamic) -> Result(a, List(JsonError))) -> Result(a, List(JsonError)) {
json_wire.decode_flags_typed(flags, decoder)
}
pub fn decode_request(data: String) -> Result(RequestEnvelope, List(JsonError)) {
json_wire.decode_request(data, contract_hash)
}
pub opaque type RpcEnvelope {
RpcEnvelope(request_id: Int, identity: String, message: Dynamic, raw_text: String)
}
pub opaque type RpcResult {
RpcResult(text: String)
}
pub fn rpc_request_id(envelope: RpcEnvelope) -> Int { envelope.request_id }
pub fn rpc_identity(envelope: RpcEnvelope) -> String { envelope.identity }
pub fn rpc_raw_payload(envelope: RpcEnvelope) -> BitArray { bit_array.from_string(envelope.raw_text) }
pub fn decode_rpc_envelope(data: BitArray) -> Result(RpcEnvelope, Nil) {
case bit_array.to_string(data) {
Error(_) -> Error(Nil)
Ok(text) -> decode_rpc_envelope_text(text)
}
}
pub fn decode_ws_rpc_envelope(msg: WebsocketMessage(a)) -> Result(RpcEnvelope, Nil) {
case msg {
mist.Text(data) ->
case decode_rpc_envelope_text(data) {
Ok(envelope) if envelope.request_id == 0 -> Error(Nil)
result -> result
}
_ -> Error(Nil)
}
}
fn decode_rpc_envelope_text(data: String) -> Result(RpcEnvelope, Nil) {
case json_wire.decode_request(data, contract_hash) {
Error(_) -> Error(Nil)
Ok(envelope) ->
case extract_message_type(envelope.message) {
Error(_) -> Error(Nil)
Ok(type_str) ->
Ok(RpcEnvelope(
request_id: envelope.request_id,
identity: type_str,
message: envelope.message,
raw_text: data,
))
}
}
}
fn extract_message_type(message: Dynamic) -> Result(String, Nil) {
case decode.run(
message,
decode.field(\"type\", decode.string, fn(x) {
decode.success(x)
}),
) {
Ok(type_str) -> Ok(type_str)
Error(_) -> Error(Nil)
}
}
"/utf8>>/binary,
Json_dispatch/binary>>/binary,
"\n"/utf8>>/binary,
Dispatch_fn/binary>>/binary,
"
pub fn send_rpc_result(conn: WebsocketConnection, result: RpcResult) -> Nil {
let _send_result = mist.send_text_frame(conn, result.text)
Nil
}
pub fn rpc_result_body(result: RpcResult) -> bytes_tree.BytesTree {
bytes_tree.from_string(result.text)
}
pub fn rpc_content_type() -> String {
\"application/json\"
}
pub fn auth_error_result(request_id: Int, message: String) -> RpcResult {
let result: Result(Nil, String) = Error(message)
let encoded =
json_codecs.json_encode_gleam_result__result(
result,
fn(_x) { json.null() },
fn(x) { json.string(x) },
)
RpcResult(text: encode_response(request_id:, value: encoded))
}
pub fn error_result(request_id: Int, message: String) -> RpcResult {
RpcResult(text: json_wire.encode_error(
request_id: Some(request_id),
errors: [JsonError(\"rpc\", message)],
))
}
pub fn malformed_rpc_result() -> RpcResult {
RpcResult(text: json_wire.encode_error(
request_id: None,
errors: [JsonError(\"frame\", \"invalid RPC frame\")],
))
}
pub fn variant_tag(_value: Dynamic) -> Result(String, Nil) {
panic as \"JSON protocol: variant_tag not implemented\"
}
pub fn encode(_value: a) -> BitArray {
panic as \"JSON protocol: encode not implemented\"
}
"/utf8>>.
-file("src/rally/generator.gleam", 816).
?DOC(" Generate the server-side protocol wire facade based on the protocol string.\n").
-spec generate_protocol_wire(
binary(),
binary(),
binary(),
binary(),
list(libero@scanner:handler_endpoint()),
gleam@option:option(rally@types:auth_config()),
binary()
) -> binary().
generate_protocol_wire(
Protocol,
Atoms_module,
Contract_hash,
Rpc_dispatch_module,
Endpoints,
Auth_config,
Wire_import_module
) ->
case Protocol of
<<"json"/utf8>> ->
json_protocol_wire_source(
Contract_hash,
Rpc_dispatch_module,
Endpoints,
Auth_config,
Wire_import_module
);
_ ->
etf_protocol_wire_source(
Atoms_module,
Rpc_dispatch_module,
Auth_config
)
end.
-file("src/rally/generator.gleam", 843).
?DOC(
" Generate the client-side JS protocol facade module.\n"
" Re-exports FFI functions from the correct libero wire module\n"
" based on protocol (ETF re-exports from rpc_ffi.mjs, JSON wraps\n"
" encode_request with the contract hash).\n"
).
-spec generate_protocol_wire_js(binary(), binary()) -> binary().
generate_protocol_wire_js(Protocol, Contract_hash) ->
case Protocol of
<<"json"/utf8>> ->
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"// Generated by Rally — do not edit.\n"/utf8,
"import { encode_request as libero_encode_request, decode_server_frame as libero_decode_server_frame, encode_flags, decode_flags_typed, identity } from \"../../libero/libero/json/wire_ffi.mjs\";\n"/utf8>>/binary,
"import { Response, Push, Error as FrameError } from \"../../libero/libero/frame.mjs\";\n"/utf8>>/binary,
"import { Ok, Error as ResultError, Empty, NonEmpty, BitArray } from \"../../gleam_stdlib/gleam.mjs\";\n"/utf8>>/binary,
"import { Some, None } from \"../../gleam_stdlib/gleam/option.mjs\";\n"/utf8>>/binary,
"import { typeRegistry } from \"./type_registry.mjs\";\n"/utf8>>/binary,
"\n"/utf8>>/binary,
"const contract_hash = \""/utf8>>/binary,
Contract_hash/binary>>/binary,
"\";\n"/utf8>>/binary,
"\n"/utf8>>/binary,
"// ---------- Libero typed JSON -> Gleam JS value ----------\n"/utf8>>/binary,
"//\n"/utf8>>/binary,
"// Response and push values arrive as Libero typed-value objects:\n"/utf8>>/binary,
"// { type: \"<module>.<Type>\", variant: \"<Variant>\", fields: ... }\n"/utf8>>/binary,
"//\n"/utf8>>/binary,
"// Container types (Result, Option) use array fields:\n"/utf8>>/binary,
"// Result Ok: { ..., fields: [inner_value] }\n"/utf8>>/binary,
"// Result Error: { ..., fields: [inner_value] }\n"/utf8>>/binary,
"// Option Some: { ..., fields: [inner_value] }\n"/utf8>>/binary,
"// Option None: { ..., fields: {} }\n"/utf8>>/binary,
"// User types: { ..., fields: { label: value, ... } }\n"/utf8>>/binary,
"\n"/utf8>>/binary,
"function typedJsonToGleamValue(value) {\n"/utf8>>/binary,
" if (value === undefined || value === null) return undefined;\n"/utf8>>/binary,
" if (typeof value !== \"object\") return value;\n"/utf8>>/binary,
" if (Array.isArray(value)) return value.map(v => typedJsonToGleamValue(v));\n"/utf8>>/binary,
" // Libero typed-value shape: { type, variant, fields }\n"/utf8>>/binary,
" if (value.type && value.variant && value.fields !== undefined) {\n"/utf8>>/binary,
" const t = value.type;\n"/utf8>>/binary,
" const v = value.variant;\n"/utf8>>/binary,
" const f = value.fields;\n"/utf8>>/binary,
" if (t === \"gleam.Nil\") return undefined;\n"/utf8>>/binary,
" if (t === \"gleam.String\") return f.value;\n"/utf8>>/binary,
" if (t === \"gleam.Int\") return f.value;\n"/utf8>>/binary,
" if (t === \"gleam.Float\") return f.value;\n"/utf8>>/binary,
" if (t === \"gleam.Bool\") return f.value;\n"/utf8>>/binary,
" if (t === \"gleam.Result\" || t === \"gleam/result.Result\") {\n"/utf8>>/binary,
" const inner = typedJsonToGleamValue(Array.isArray(f) ? f[0] : f.value);\n"/utf8>>/binary,
" return v === \"Ok\" ? new Ok(inner) : new ResultError(inner);\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" if (t === \"gleam.Option\" || t === \"gleam/option.Option\") {\n"/utf8>>/binary,
" if (v === \"None\" || (Array.isArray(f) && f.length === 0) || Object.keys(f).length === 0) return new None();\n"/utf8>>/binary,
" const inner = typedJsonToGleamValue(Array.isArray(f) ? f[0] : f.value);\n"/utf8>>/binary,
" return new Some(inner);\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" if (t === \"gleam.List\") {\n"/utf8>>/binary,
" if (v === \"Empty\") return new Empty();\n"/utf8>>/binary,
" const items = (f.items || f).map(item => typedJsonToGleamValue(item));\n"/utf8>>/binary,
" let list = new Empty();\n"/utf8>>/binary,
" for (let i = items.length - 1; i >= 0; i--) list = new NonEmpty(items[i], list);\n"/utf8>>/binary,
" return list;\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" if (t === \"gleam.Tuple\") {\n"/utf8>>/binary,
" return (f.elements || f).map(e => typedJsonToGleamValue(e));\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" // User types: dispatch by full type identity\n"/utf8>>/binary,
" // Key by full \"type\" + \"#\" + \"variant\" so a mismatched parent type\n"/utf8>>/binary,
" // (\"some/module.OldType\" with variant \"Discount\") never resolves to\n"/utf8>>/binary,
" // the registry entry for \"some/module.Discount\".\n"/utf8>>/binary,
" const key = t + \"#\" + v;\n"/utf8>>/binary,
" const ctor = typeRegistry[key];\n"/utf8>>/binary,
" if (!ctor) {\n"/utf8>>/binary,
" throw new Error(\"Unknown type in JSON decode: type=\" + t + \" variant=\" + v + \" (lookup key: \" + key + \")\");\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" if (Array.isArray(f)) {\n"/utf8>>/binary,
" return ctor(f.map(val => typedJsonToGleamValue(val)));\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" const fields = {};\n"/utf8>>/binary,
" for (const [fieldKey, val] of Object.entries(f)) {\n"/utf8>>/binary,
" fields[fieldKey] = typedJsonToGleamValue(val);\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" return ctor(fields);\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" return value;\n"/utf8>>/binary,
"}\n"/utf8>>/binary,
"\n"/utf8>>/binary,
"// ---------- Public API ----------\n"/utf8>>/binary,
"//\n"/utf8>>/binary,
"// encode_request receives messages pre-encoded by Gleam typed JSON\n"/utf8>>/binary,
"// codecs (types.json_encode_client_msg). The value is already in\n"/utf8>>/binary,
"// the Libero typed-value shape — pass through without conversion.\n"/utf8>>/binary,
"\n"/utf8>>/binary,
"export function encode_request(module, requestId, msg) {\n"/utf8>>/binary,
" return libero_encode_request(module, requestId, msg, contract_hash);\n"/utf8>>/binary,
"}\n"/utf8>>/binary,
"\n"/utf8>>/binary,
"export function decode_server_frame(data) {\n"/utf8>>/binary,
" try {\n"/utf8>>/binary,
" const result = libero_decode_server_frame(data);\n"/utf8>>/binary,
" if (result instanceof ResultError) return result;\n"/utf8>>/binary,
" const frame = result[0];\n"/utf8>>/binary,
" if (frame instanceof Response) {\n"/utf8>>/binary,
" try {\n"/utf8>>/binary,
" return new Ok({ kind: \"response\", requestId: frame.request_id, value: typedJsonToGleamValue(frame.value) });\n"/utf8>>/binary,
" } catch (e) {\n"/utf8>>/binary,
" return new Ok({ kind: \"error\", requestId: frame.request_id, errors: [[\"decode\", e.message || \"JSON decode error\"]] });\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" if (frame instanceof Push) {\n"/utf8>>/binary,
" try {\n"/utf8>>/binary,
" return new Ok({ kind: \"push\", module: frame.module, value: typedJsonToGleamValue(frame.value) });\n"/utf8>>/binary,
" } catch (e) {\n"/utf8>>/binary,
" return new ResultError(e.message || \"JSON decode error\");\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" if (frame instanceof FrameError) {\n"/utf8>>/binary,
" const rid = frame.request_id;\n"/utf8>>/binary,
" return new Ok({ kind: \"error\", requestId: rid instanceof Some ? rid[0] : null, errors: frame.errors });\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
" return result;\n"/utf8>>/binary,
" } catch (e) {\n"/utf8>>/binary,
" return new ResultError(e.message || \"JSON decode error\");\n"/utf8>>/binary,
" }\n"/utf8>>/binary,
"}\n"/utf8>>/binary,
"\n"/utf8>>/binary,
"export { encode_flags, decode_flags_typed, identity, typedJsonToGleamValue };\n"/utf8>>;
_ ->
<<<<"// Generated by Rally — do not edit.\n"/utf8,
"export { encode_request, decode_server_frame, identity } from \"../../libero/libero/rpc_ffi.mjs\";\n"/utf8>>/binary,
"export { encode_flags, decode_flags_typed } from \"../../libero/libero/wire.mjs\";\n"/utf8>>
end.