-module(lightspeed@router). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/lightspeed/router.gleam"). -export([new/1, add/4, resolve/2, mount_instructions/2, decode/2, view_id/1, params/1, match_label/1]). -export_type([route_param/0, route/1, router/1, route_match/1, matched_route/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(" Router integration helpers for mount/render flows.\n"). -type route_param() :: {route_param, binary(), binary()}. -type route(LKJ) :: {route, binary(), binary(), fun((lightspeed@event:inbound_event(), list(route_param())) -> {ok, LKJ} | {error, lightspeed@event:decode_error()})}. -type router(LKK) :: {router, binary(), list(route(LKK))}. -type route_match(LKL) :: {found, matched_route(LKL)} | {not_found, binary(), binary()}. -type matched_route(LKM) :: {matched_route, binary(), binary(), binary(), list(route_param()), fun((lightspeed@event:inbound_event(), list(route_param())) -> {ok, LKM} | {error, lightspeed@event:decode_error()})}. -file("src/lightspeed/router.gleam", 48). ?DOC(" Create a new router with a fallback not-found view.\n"). -spec new(binary()) -> router(any()). new(Not_found_view) -> {router, Not_found_view, []}. -file("src/lightspeed/router.gleam", 53). ?DOC(" Register a route pattern.\n"). -spec add( router(LKP), binary(), binary(), fun((lightspeed@event:inbound_event(), list(route_param())) -> {ok, LKP} | {error, lightspeed@event:decode_error()}) ) -> router(LKP). add(Router, Pattern, View_id, Decode_event) -> {router, erlang:element(2, Router), [{route, Pattern, View_id, Decode_event} | erlang:element(3, Router)]}. -file("src/lightspeed/router.gleam", 229). -spec join_graphemes(list(binary())) -> binary(). join_graphemes(Graphemes) -> case Graphemes of [] -> <<""/utf8>>; [Grapheme | Rest] -> <> end. -file("src/lightspeed/router.gleam", 217). -spec param_name(binary()) -> gleam@option:option(binary()). 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/router.gleam", 173). -spec match_segments(list(binary()), list(binary()), list(route_param())) -> gleam@option:option(list(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 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/router.gleam", 206). -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/router.gleam", 200). -spec segments(binary()) -> list(binary()). segments(Path) -> _pipe = Path, _pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>), filter_empty(_pipe@1, []). -file("src/lightspeed/router.gleam", 167). -spec match_pattern(binary(), binary()) -> gleam@option:option(list(route_param())). match_pattern(Pattern, Path) -> Pattern_segments = segments(Pattern), Path_segments = segments(Path), match_segments(Pattern_segments, Path_segments, []). -file("src/lightspeed/router.gleam", 119). -spec find_match(list(route(LLM)), binary()) -> gleam@option:option(matched_route(LLM)). find_match(Routes, Normalized_path) -> case Routes of [] -> none; [Route | Rest] -> case match_pattern(erlang:element(2, Route), Normalized_path) of {some, Captures} -> {some, {matched_route, Normalized_path, erlang:element(2, Route), erlang:element(3, Route), Captures, erlang:element(4, Route)}}; none -> find_match(Rest, Normalized_path) end end. -file("src/lightspeed/router.gleam", 243). -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/router.gleam", 236). -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/router.gleam", 156). -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/router.gleam", 142). -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/router.gleam", 67). ?DOC(" Resolve one path into a route match.\n"). -spec resolve(router(LKV), binary()) -> route_match(LKV). resolve(Router, Path) -> Normalized = normalize_path(Path), case find_match(lists:reverse(erlang:element(3, Router)), Normalized) of {some, Matched} -> {found, Matched}; none -> {not_found, Normalized, erlang:element(2, Router)} end. -file("src/lightspeed/router.gleam", 77). ?DOC(" Build a mount/render ISA sequence for a resolved route.\n"). -spec mount_instructions(route_match(any()), binary()) -> list(lightspeed@agent@isa:instruction()). mount_instructions(Route_match, Csrf_token) -> case Route_match of {found, Matched} -> [{mount, erlang:element(2, Matched), Csrf_token}, {render, erlang:element(4, Matched)}]; {not_found, Path, View_id} -> [{mount, Path, Csrf_token}, {render, View_id}] end. -file("src/lightspeed/router.gleam", 94). ?DOC(" Decode one inbound event against a matched route decoder.\n"). -spec decode(matched_route(LLB), lightspeed@event:inbound_event()) -> {ok, LLB} | {error, lightspeed@event:decode_error()}. decode(Matched, Inbound) -> (erlang:element(6, Matched))(Inbound, erlang:element(5, Matched)). -file("src/lightspeed/router.gleam", 102). ?DOC(" Matched route view id.\n"). -spec view_id(matched_route(any())) -> binary(). view_id(Matched) -> erlang:element(4, Matched). -file("src/lightspeed/router.gleam", 107). ?DOC(" Matched route captured params.\n"). -spec params(matched_route(any())) -> list(route_param()). params(Matched) -> erlang:element(5, Matched). -file("src/lightspeed/router.gleam", 112). ?DOC(" Render stable route match labels for logs.\n"). -spec match_label(route_match(any())) -> binary(). match_label(Route_match) -> case Route_match of {found, Matched} -> <<"found:"/utf8, (erlang:element(3, Matched))/binary>>; {not_found, _, View_id} -> <<"not_found:"/utf8, View_id/binary>> end.