-module(lightspeed@framework@endpoint).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lightspeed/framework/endpoint.gleam").
-export([new/2, pipe/2, get_controller/3, get_controller_indexed/3, get_live/4, get_live_indexed/4, static/4, call/2, route_labels/1, static_labels/1]).
-export_type([route/0, static_asset/0, endpoint/0, dispatch/0]).
-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(" Endpoint + middleware pipeline with controller and live-route dispatch.\n").
-type route() :: {controller_route,
lightspeed@framework@http:method(),
binary(),
fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn())} |
{live_route,
lightspeed@framework@http:method(),
binary(),
binary(),
fun((lightspeed@framework@http:conn()) -> binary())}.
-type static_asset() :: {static_asset, binary(), binary(), binary()}.
-opaque endpoint() :: {endpoint,
binary(),
lightspeed@transport@contract:auth_hook(),
list(fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn())),
list(route()),
list(static_asset())}.
-type dispatch() :: {controller_dispatch,
fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn()),
list(lightspeed@framework@http:route_param())} |
{live_dispatch,
binary(),
fun((lightspeed@framework@http:conn()) -> binary()),
list(lightspeed@framework@http:route_param())}.
-file("src/lightspeed/framework/endpoint.gleam", 58).
?DOC(" Create an endpoint.\n").
-spec new(lightspeed@transport@contract:auth_hook(), binary()) -> endpoint().
new(Auth_hook, Websocket_path) ->
{endpoint, Websocket_path, Auth_hook, [], [], []}.
-file("src/lightspeed/framework/endpoint.gleam", 69).
?DOC(" Register one middleware in endpoint order.\n").
-spec pipe(
endpoint(),
fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn())
) -> endpoint().
pipe(Endpoint, Middleware) ->
{endpoint,
erlang:element(2, Endpoint),
erlang:element(3, Endpoint),
[Middleware | erlang:element(4, Endpoint)],
erlang:element(5, Endpoint),
erlang:element(6, Endpoint)}.
-file("src/lightspeed/framework/endpoint.gleam", 74).
?DOC(" Register a GET controller route from a verified route helper.\n").
-spec get_controller(
endpoint(),
lightspeed@framework@verified_routes:verified_route(any()),
fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn())
) -> endpoint().
get_controller(Endpoint, Route, Run) ->
Pattern = lightspeed@framework@verified_routes:pattern(Route),
{endpoint,
erlang:element(2, Endpoint),
erlang:element(3, Endpoint),
erlang:element(4, Endpoint),
[{controller_route, get, Pattern, Run} | erlang:element(5, Endpoint)],
erlang:element(6, Endpoint)}.
-file("src/lightspeed/framework/endpoint.gleam", 87).
?DOC(" Register a GET controller route from one router-indexed verified route.\n").
-spec get_controller_indexed(
endpoint(),
lightspeed@framework@verified_routes:indexed_route(any(), lightspeed@framework@verified_routes:get_tag()),
fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn())
) -> endpoint().
get_controller_indexed(Endpoint, Route, Run) ->
get_controller(
Endpoint,
lightspeed@framework@verified_routes:as_verified_route(Route),
Run
).
-file("src/lightspeed/framework/endpoint.gleam", 96).
?DOC(" Register a GET live route from a verified route helper.\n").
-spec get_live(
endpoint(),
lightspeed@framework@verified_routes:verified_route(any()),
binary(),
fun((lightspeed@framework@http:conn()) -> binary())
) -> endpoint().
get_live(Endpoint, Route, View_id, Render) ->
Pattern = lightspeed@framework@verified_routes:pattern(Route),
{endpoint,
erlang:element(2, Endpoint),
erlang:element(3, Endpoint),
erlang:element(4, Endpoint),
[{live_route, get, Pattern, View_id, Render} |
erlang:element(5, Endpoint)],
erlang:element(6, Endpoint)}.
-file("src/lightspeed/framework/endpoint.gleam", 115).
?DOC(" Register a GET live route from one router-indexed verified route.\n").
-spec get_live_indexed(
endpoint(),
lightspeed@framework@verified_routes:indexed_route(any(), lightspeed@framework@verified_routes:get_tag()),
binary(),
fun((lightspeed@framework@http:conn()) -> binary())
) -> endpoint().
get_live_indexed(Endpoint, Route, View_id, Render) ->
get_live(
Endpoint,
lightspeed@framework@verified_routes:as_verified_route(Route),
View_id,
Render
).
-file("src/lightspeed/framework/endpoint.gleam", 420).
-spec join_graphemes(list(binary())) -> binary().
join_graphemes(Graphemes) ->
case Graphemes of
[] ->
<<""/utf8>>;
[Grapheme | Rest] ->
<<Grapheme/binary, (join_graphemes(Rest))/binary>>
end.
-file("src/lightspeed/framework/endpoint.gleam", 413).
-spec drop_last(binary()) -> binary().
drop_last(Path) ->
case lists:reverse(gleam@string:to_graphemes(Path)) of
[] ->
<<""/utf8>>;
[_ | Rest] ->
_pipe = Rest,
_pipe@1 = lists:reverse(_pipe),
join_graphemes(_pipe@1)
end.
-file("src/lightspeed/framework/endpoint.gleam", 406).
-spec ends_with_slash(binary()) -> boolean().
ends_with_slash(Path) ->
case lists:reverse(gleam@string:to_graphemes(Path)) of
[<<"/"/utf8>> | _] ->
true;
_ ->
false
end.
-file("src/lightspeed/framework/endpoint.gleam", 378).
-spec trim_trailing_slashes(binary()) -> binary().
trim_trailing_slashes(Path) ->
case Path of
<<"/"/utf8>> ->
<<"/"/utf8>>;
_ ->
case ends_with_slash(Path) of
true ->
trim_trailing_slashes(drop_last(Path));
false ->
Path
end
end.
-file("src/lightspeed/framework/endpoint.gleam", 364).
-spec normalize_path(binary()) -> binary().
normalize_path(Path) ->
No_query = case gleam@string:split(Path, <<"?"/utf8>>) of
[First | _] ->
First;
[] ->
Path
end,
Trimmed = trim_trailing_slashes(No_query),
case Trimmed of
<<""/utf8>> ->
<<"/"/utf8>>;
_ ->
Trimmed
end.
-file("src/lightspeed/framework/endpoint.gleam", 125).
?DOC(" Register one static asset.\n").
-spec static(endpoint(), binary(), binary(), binary()) -> endpoint().
static(Endpoint, Path, Content_type, Body) ->
{endpoint,
erlang:element(2, Endpoint),
erlang:element(3, Endpoint),
erlang:element(4, Endpoint),
erlang:element(5, Endpoint),
[{static_asset, normalize_path(Path), Content_type, Body} |
erlang:element(6, Endpoint)]}.
-file("src/lightspeed/framework/endpoint.gleam", 267).
-spec render_live(
lightspeed@framework@http:conn(),
endpoint(),
binary(),
fun((lightspeed@framework@http:conn()) -> binary())
) -> lightspeed@framework@http:conn().
render_live(Conn, Endpoint, View_id, Render) ->
Request = {html_request,
lightspeed@framework@http:request_session_id(Conn),
normalize_path(lightspeed@framework@http:request_path(Conn)),
lightspeed@framework@http:request_csrf_token(Conn),
lightspeed@framework@http:request_origin(Conn)},
case lightspeed@transport@wisp_html:render_initial(
Request,
Render(Conn),
erlang:element(2, Endpoint),
erlang:element(3, Endpoint)
) of
{ok, Response} ->
_pipe = Conn,
_pipe@1 = lightspeed@framework@http:send(
_pipe,
lightspeed@transport@wisp_html:status(Response),
<<"text/html; charset=utf-8"/utf8>>,
lightspeed@transport@wisp_html:body(Response)
),
lightspeed@framework@http:put_header(
_pipe@1,
<<"x-lightspeed-view-id"/utf8>>,
View_id
);
{error, Error} ->
lightspeed@framework@controller:internal_error(
Conn,
lightspeed@transport@contract:error_to_string(Error)
)
end.
-file("src/lightspeed/framework/endpoint.gleam", 352).
-spec route_param_name(binary()) -> gleam@option:option(binary()).
route_param_name(Segment) ->
case gleam@string:to_graphemes(Segment) of
[<<":"/utf8>> | Rest] ->
case Rest of
[] ->
none;
_ ->
{some, join_graphemes(Rest)}
end;
_ ->
none
end.
-file("src/lightspeed/framework/endpoint.gleam", 325).
-spec match_segments(
list(binary()),
list(binary()),
list(lightspeed@framework@http:route_param())
) -> gleam@option:option(list(lightspeed@framework@http:route_param())).
match_segments(Pattern_segments, Path_segments, Captures_rev) ->
case {Pattern_segments, Path_segments} of
{[], []} ->
{some, lists:reverse(Captures_rev)};
{[], _} ->
none;
{_, []} ->
none;
{[Pattern_segment | Pattern_rest], [Path_segment | Path_rest]} ->
case route_param_name(Pattern_segment) of
{some, Name} ->
match_segments(
Pattern_rest,
Path_rest,
[{route_param, Name, Path_segment} | Captures_rev]
);
none ->
case Pattern_segment =:= Path_segment of
true ->
match_segments(
Pattern_rest,
Path_rest,
Captures_rev
);
false ->
none
end
end
end.
-file("src/lightspeed/framework/endpoint.gleam", 395).
-spec filter_empty(list(binary()), list(binary())) -> list(binary()).
filter_empty(Values, Kept_rev) ->
case Values of
[] ->
lists:reverse(Kept_rev);
[Value | Rest] ->
case Value =:= <<""/utf8>> of
true ->
filter_empty(Rest, Kept_rev);
false ->
filter_empty(Rest, [Value | Kept_rev])
end
end.
-file("src/lightspeed/framework/endpoint.gleam", 389).
-spec segments(binary()) -> list(binary()).
segments(Path) ->
_pipe = Path,
_pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>),
filter_empty(_pipe@1, []).
-file("src/lightspeed/framework/endpoint.gleam", 316).
-spec match_pattern(binary(), binary()) -> gleam@option:option(list(lightspeed@framework@http:route_param())).
match_pattern(Pattern, Path) ->
Pattern_segments = segments(Pattern),
Path_segments = segments(Path),
match_segments(Pattern_segments, Path_segments, []).
-file("src/lightspeed/framework/endpoint.gleam", 312).
-spec method_matches(
lightspeed@framework@http:method(),
lightspeed@framework@http:method()
) -> boolean().
method_matches(Expected, Actual) ->
lightspeed@framework@http:method_label(Expected) =:= lightspeed@framework@http:method_label(
Actual
).
-file("src/lightspeed/framework/endpoint.gleam", 230).
-spec find_dispatch_loop(
list(route()),
lightspeed@framework@http:method(),
binary()
) -> gleam@option:option(dispatch()).
find_dispatch_loop(Routes, Method, Path) ->
case Routes of
[] ->
none;
[Route | Rest] ->
case Route of
{controller_route, Route_method, Pattern, Run} ->
case {method_matches(Route_method, Method),
match_pattern(Pattern, Path)} of
{true, {some, Params}} ->
{some, {controller_dispatch, Run, Params}};
{_, _} ->
find_dispatch_loop(Rest, Method, Path)
end;
{live_route, Route_method@1, Pattern@1, View_id, Render} ->
case {method_matches(Route_method@1, Method),
match_pattern(Pattern@1, Path)} of
{true, {some, Params@1}} ->
{some, {live_dispatch, View_id, Render, Params@1}};
{_, _} ->
find_dispatch_loop(Rest, Method, Path)
end
end
end.
-file("src/lightspeed/framework/endpoint.gleam", 224).
-spec find_dispatch(endpoint(), lightspeed@framework@http:conn()) -> gleam@option:option(dispatch()).
find_dispatch(Endpoint, Conn) ->
Method = lightspeed@framework@http:request_method(Conn),
Path = normalize_path(lightspeed@framework@http:request_path(Conn)),
_pipe = erlang:element(5, Endpoint),
_pipe@1 = lists:reverse(_pipe),
find_dispatch_loop(_pipe@1, Method, Path).
-file("src/lightspeed/framework/endpoint.gleam", 210).
-spec find_static(list(static_asset()), binary()) -> gleam@option:option(static_asset()).
find_static(Static_assets, Path) ->
case Static_assets of
[] ->
none;
[Asset | Rest] ->
case erlang:element(2, Asset) =:= Path of
true ->
{some, Asset};
false ->
find_static(Rest, Path)
end
end.
-file("src/lightspeed/framework/endpoint.gleam", 196).
-spec serve_static(endpoint(), lightspeed@framework@http:conn()) -> gleam@option:option(lightspeed@framework@http:conn()).
serve_static(Endpoint, Conn) ->
case lightspeed@framework@http:request_method(Conn) of
get ->
Path = normalize_path(lightspeed@framework@http:request_path(Conn)),
case find_static(erlang:element(6, Endpoint), Path) of
{some, Asset} ->
{some,
lightspeed@framework@http:send(
Conn,
200,
erlang:element(3, Asset),
erlang:element(4, Asset)
)};
none ->
none
end;
_ ->
none
end.
-file("src/lightspeed/framework/endpoint.gleam", 185).
-spec run_middleware(
lightspeed@framework@http:conn(),
list(fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn()))
) -> lightspeed@framework@http:conn().
run_middleware(Conn, Reversed) ->
case Reversed of
[] ->
Conn;
[Middleware | Rest] ->
case lightspeed@framework@http:halted(Conn) of
true ->
Conn;
false ->
run_middleware(Middleware(Conn), Rest)
end
end.
-file("src/lightspeed/framework/endpoint.gleam", 142).
?DOC(" Handle one request through middleware and dispatch tables.\n").
-spec call(endpoint(), lightspeed@framework@http:request()) -> lightspeed@framework@http:response().
call(Endpoint, Request) ->
Conn = run_middleware(
begin
_pipe = Request,
lightspeed@framework@http:from_request(_pipe)
end,
erlang:element(4, Endpoint)
),
case lightspeed@framework@http:halted(Conn) of
true ->
lightspeed@framework@http:to_response(Conn);
false ->
case serve_static(Endpoint, Conn) of
{some, Served} ->
lightspeed@framework@http:to_response(Served);
none ->
case find_dispatch(Endpoint, Conn) of
{some, {controller_dispatch, Run, Params}} ->
_pipe@1 = Conn,
_pipe@2 = lightspeed@framework@http:with_route_params(
_pipe@1,
Params
),
_pipe@3 = Run(_pipe@2),
lightspeed@framework@http:to_response(_pipe@3);
{some, {live_dispatch, View_id, Render, Params@1}} ->
_pipe@4 = Conn,
_pipe@5 = lightspeed@framework@http:with_route_params(
_pipe@4,
Params@1
),
_pipe@6 = render_live(
_pipe@5,
Endpoint,
View_id,
Render
),
lightspeed@framework@http:to_response(_pipe@6);
none ->
_pipe@7 = Conn,
_pipe@8 = lightspeed@framework@controller:not_found(
_pipe@7
),
lightspeed@framework@http:to_response(_pipe@8)
end
end
end.
-file("src/lightspeed/framework/endpoint.gleam", 303).
-spec route_label(route()) -> binary().
route_label(Route) ->
case Route of
{controller_route, Method, Pattern, _} ->
<<<<<<"controller:"/utf8,
(lightspeed@framework@http:method_label(Method))/binary>>/binary,
":"/utf8>>/binary,
Pattern/binary>>;
{live_route, Method@1, Pattern@1, View_id, _} ->
<<<<<<<<<<"live:"/utf8,
(lightspeed@framework@http:method_label(
Method@1
))/binary>>/binary,
":"/utf8>>/binary,
Pattern@1/binary>>/binary,
":"/utf8>>/binary,
View_id/binary>>
end.
-file("src/lightspeed/framework/endpoint.gleam", 172).
?DOC(" Stable endpoint route table labels.\n").
-spec route_labels(endpoint()) -> list(binary()).
route_labels(Endpoint) ->
_pipe = erlang:element(5, Endpoint),
_pipe@1 = lists:reverse(_pipe),
gleam@list:map(_pipe@1, fun route_label/1).
-file("src/lightspeed/framework/endpoint.gleam", 179).
?DOC(" Stable static table labels.\n").
-spec static_labels(endpoint()) -> list(binary()).
static_labels(Endpoint) ->
_pipe = erlang:element(6, Endpoint),
_pipe@1 = lists:reverse(_pipe),
gleam@list:map(
_pipe@1,
fun(Asset) -> <<"static:"/utf8, (erlang:element(2, Asset))/binary>> end
).