-module(proute@discover).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/proute/discover.gleam").
-export([discover_mount/1, describe_error/1, page_module_path/1]).
-export_type([mount_routes/0, page_route/0, route_kind/0, route_segment/0, route_param/0, discover_error/0]).
-type mount_routes() :: {mount_routes,
proute@config:mount(),
list(page_route())}.
-type page_route() :: {page_route,
route_kind(),
binary(),
binary(),
list(route_segment()),
list(route_param()),
binary(),
binary()}.
-type route_kind() :: home | not_found | static | dynamic.
-type route_segment() :: {static_segment, binary()} |
{dynamic_segment, binary()}.
-type route_param() :: {route_param, binary(), binary()}.
-type discover_error() :: {pages_directory_unreadable, binary()} |
{missing_not_found, binary(), binary()} |
{unsupported_catch_all, binary()} |
{invalid_page_module_path, binary()} |
{invalid_page_segment, binary(), binary()} |
{invalid_route_param, binary(), binary()} |
{duplicate_route_param, binary(), binary()} |
{invalid_constructor, binary(), binary()} |
{duplicate_path, binary(), binary(), binary()} |
{duplicate_constructor, binary(), binary(), binary()} |
{duplicate_helper, binary(), binary(), binary()}.
-file("src/proute/discover.gleam", 453).
-spec route_segment_sort_key(route_segment()) -> binary().
route_segment_sort_key(Segment) ->
case Segment of
{static_segment, Value} ->
<<"1:"/utf8, Value/binary>>;
{dynamic_segment, _} ->
<<"2:"/utf8>>
end.
-file("src/proute/discover.gleam", 436).
-spec route_sort_key(page_route()) -> binary().
route_sort_key(Route) ->
case erlang:element(2, Route) of
home ->
<<"0:"/utf8, (erlang:element(4, Route))/binary>>;
static ->
<<<<<<"1:"/utf8,
(begin
_pipe = erlang:element(5, Route),
_pipe@1 = gleam@list:map(
_pipe,
fun route_segment_sort_key/1
),
_pipe@2 = (fun(Keys) ->
lists:append(Keys, [<<"0"/utf8>>])
end)(_pipe@1),
gleam@string:join(_pipe@2, <<"/"/utf8>>)
end)/binary>>/binary,
":"/utf8>>/binary,
(erlang:element(4, Route))/binary>>;
dynamic ->
<<<<<<"1:"/utf8,
(begin
_pipe = erlang:element(5, Route),
_pipe@1 = gleam@list:map(
_pipe,
fun route_segment_sort_key/1
),
_pipe@2 = (fun(Keys) ->
lists:append(Keys, [<<"0"/utf8>>])
end)(_pipe@1),
gleam@string:join(_pipe@2, <<"/"/utf8>>)
end)/binary>>/binary,
":"/utf8>>/binary,
(erlang:element(4, Route))/binary>>;
not_found ->
<<"2:"/utf8, (erlang:element(4, Route))/binary>>
end.
-file("src/proute/discover.gleam", 431).
-spec sort_routes(list(page_route())) -> list(page_route()).
sort_routes(Routes) ->
_pipe = Routes,
gleam@list:sort(
_pipe,
fun(A, B) ->
gleam@string:compare(route_sort_key(A), route_sort_key(B))
end
).
-file("src/proute/discover.gleam", 406).
-spec reject_duplicate_helpers(
list(page_route()),
gleam@dict:dict(binary(), page_route())
) -> {ok, nil} | {error, discover_error()}.
reject_duplicate_helpers(Routes, Seen) ->
case Routes of
[] ->
{ok, nil};
[Route | Rest] ->
Helper = proute@names:helper_name(erlang:element(3, Route)),
case {proute@names:is_valid_label(Helper),
gleam_stdlib:map_get(Seen, Helper)} of
{false, _} ->
{error,
{invalid_constructor,
erlang:element(7, Route),
erlang:element(3, Route)}};
{true, {ok, First}} ->
{error,
{duplicate_helper,
Helper,
erlang:element(7, First),
erlang:element(7, Route)}};
{true, {error, _}} ->
reject_duplicate_helpers(
Rest,
gleam@dict:insert(Seen, Helper, Route)
)
end
end.
-file("src/proute/discover.gleam", 383).
-spec reject_duplicate_constructors(
list(page_route()),
gleam@dict:dict(binary(), page_route())
) -> {ok, nil} | {error, discover_error()}.
reject_duplicate_constructors(Routes, Seen) ->
case Routes of
[] ->
{ok, nil};
[Route | Rest] ->
case gleam_stdlib:map_get(Seen, erlang:element(3, Route)) of
{ok, First} ->
{error,
{duplicate_constructor,
erlang:element(3, Route),
erlang:element(7, First),
erlang:element(7, Route)}};
{error, _} ->
reject_duplicate_constructors(
Rest,
gleam@dict:insert(Seen, erlang:element(3, Route), Route)
)
end
end.
-file("src/proute/discover.gleam", 537).
-spec route_pattern_path(page_route()) -> binary().
route_pattern_path(Route) ->
_pipe = erlang:element(4, Route),
_pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>),
_pipe@2 = gleam@list:map(
_pipe@1,
fun(Segment) ->
case gleam_stdlib:string_starts_with(Segment, <<":"/utf8>>) of
true ->
<<":_"/utf8>>;
false ->
Segment
end
end
),
gleam@string:join(_pipe@2, <<"/"/utf8>>).
-file("src/proute/discover.gleam", 351).
-spec reject_duplicate_paths(
list(page_route()),
gleam@dict:dict(binary(), page_route())
) -> {ok, nil} | {error, discover_error()}.
reject_duplicate_paths(Routes, Seen) ->
case Routes of
[] ->
{ok, nil};
[Route | Rest] ->
Key = route_pattern_path(Route),
case gleam_stdlib:map_get(Seen, Key) of
{ok, First} ->
{error,
{duplicate_path,
Key,
erlang:element(7, First),
erlang:element(7, Route)}};
{error, _} ->
reject_duplicate_paths(
Rest,
gleam@dict:insert(Seen, Key, Route)
)
end
end.
-file("src/proute/discover.gleam", 373).
-spec require_not_found(proute@config:mount(), list(page_route())) -> {ok, nil} |
{error, discover_error()}.
require_not_found(Mount, Routes) ->
case gleam@list:any(
Routes,
fun(Route) -> erlang:element(2, Route) =:= not_found end
) of
true ->
{ok, nil};
false ->
{error,
{missing_not_found,
erlang:element(2, Mount),
erlang:element(3, Mount)}}
end.
-file("src/proute/discover.gleam", 520).
-spec validate_params(
binary(),
list(route_param()),
gleam@dict:dict(binary(), nil)
) -> {ok, nil} | {error, discover_error()}.
validate_params(Source_file, Params, Seen) ->
case Params of
[] ->
{ok, nil};
[Param | Rest] ->
case {proute@names:is_valid_label(erlang:element(2, Param)),
gleam@dict:has_key(Seen, erlang:element(2, Param))} of
{false, _} ->
{error,
{invalid_route_param,
Source_file,
erlang:element(2, Param)}};
{true, true} ->
{error,
{duplicate_route_param,
Source_file,
erlang:element(2, Param)}};
{true, false} ->
validate_params(
Source_file,
Rest,
gleam@dict:insert(Seen, erlang:element(2, Param), nil)
)
end
end.
-file("src/proute/discover.gleam", 510).
-spec validate_constructor(binary(), binary()) -> {ok, nil} |
{error, discover_error()}.
validate_constructor(Source_file, Constructor) ->
case proute@names:is_valid_type_name(Constructor) of
true ->
{ok, nil};
false ->
{error, {invalid_constructor, Source_file, Constructor}}
end.
-file("src/proute/discover.gleam", 502).
-spec validate_route(binary(), page_route()) -> {ok, nil} |
{error, discover_error()}.
validate_route(Source_file, Route) ->
gleam@result:'try'(
validate_constructor(Source_file, erlang:element(3, Route)),
fun(_) ->
validate_params(Source_file, erlang:element(6, Route), maps:new())
end
).
-file("src/proute/discover.gleam", 300).
-spec dynamic_params(list(route_segment())) -> list(route_param()).
dynamic_params(Segments) ->
_pipe = Segments,
gleam@list:filter_map(_pipe, fun(Segment) -> case Segment of
{dynamic_segment, Name} ->
{ok, {route_param, Name, <<"String"/utf8>>}};
{static_segment, _} ->
{error, nil}
end end).
-file("src/proute/discover.gleam", 344).
-spec route_segment_path(route_segment()) -> binary().
route_segment_path(Segment) ->
case Segment of
{static_segment, Value} ->
Value;
{dynamic_segment, Name} ->
<<":"/utf8, Name/binary>>
end.
-file("src/proute/discover.gleam", 330).
-spec route_path(binary(), list(route_segment())) -> binary().
route_path(Route_root, Segments) ->
Suffix = begin
_pipe = Segments,
_pipe@1 = gleam@list:map(_pipe, fun route_segment_path/1),
gleam@string:join(_pipe@1, <<"/"/utf8>>)
end,
case {Route_root, Suffix} of
{<<"/"/utf8>>, <<""/utf8>>} ->
<<"/"/utf8>>;
{<<"/"/utf8>>, Suffix@1} ->
<<"/"/utf8, Suffix@1/binary>>;
{Route_root@1, <<""/utf8>>} ->
Route_root@1;
{Route_root@2, Suffix@2} ->
<<<<Route_root@2/binary, "/"/utf8>>/binary, Suffix@2/binary>>
end.
-file("src/proute/discover.gleam", 293).
-spec is_dynamic_segment(route_segment()) -> boolean().
is_dynamic_segment(Segment) ->
case Segment of
{dynamic_segment, _} ->
true;
{static_segment, _} ->
false
end.
-file("src/proute/discover.gleam", 282).
-spec route_kind(list(route_segment())) -> route_kind().
route_kind(Segments) ->
case Segments of
[] ->
home;
_ ->
case gleam@list:any(Segments, fun is_dynamic_segment/1) of
true ->
dynamic;
false ->
static
end
end.
-file("src/proute/discover.gleam", 319).
-spec default_constructor(binary(), binary()) -> binary().
default_constructor(Value, Default) ->
case Value of
<<""/utf8>> ->
Default;
_ ->
Value
end.
-file("src/proute/discover.gleam", 469).
-spec drop_suffix(binary(), binary()) -> binary().
drop_suffix(Value, Suffix) ->
case gleam_stdlib:string_ends_with(Value, Suffix) of
true ->
gleam@string:drop_end(Value, string:length(Suffix));
false ->
Value
end.
-file("src/proute/discover.gleam", 326).
-spec constructor_word(binary()) -> binary().
constructor_word(Segment) ->
drop_suffix(Segment, <<"_"/utf8>>).
-file("src/proute/discover.gleam", 310).
-spec constructor_name(list(binary())) -> binary().
constructor_name(Raw_segments) ->
_pipe = Raw_segments,
_pipe@1 = gleam@list:filter(
_pipe,
fun(Segment) -> Segment /= <<"home_"/utf8>> end
),
_pipe@2 = gleam@list:map(_pipe@1, fun constructor_word/1),
_pipe@3 = gleam@string:join(_pipe@2, <<"_"/utf8>>),
_pipe@4 = proute@names:pascal_case(_pipe@3),
default_constructor(_pipe@4, <<"Home"/utf8>>).
-file("src/proute/discover.gleam", 275).
-spec route_segment(binary()) -> route_segment().
route_segment(Segment) ->
case gleam_stdlib:string_ends_with(Segment, <<"_"/utf8>>) of
true ->
{dynamic_segment, drop_suffix(Segment, <<"_"/utf8>>)};
false ->
{static_segment, Segment}
end.
-file("src/proute/discover.gleam", 264).
-spec route_segments(list(binary())) -> list(route_segment()).
route_segments(Raw_segments) ->
_pipe = Raw_segments,
_pipe@1 = gleam@list:index_map(
_pipe,
fun(Segment, Index) ->
case (Segment =:= <<"home_"/utf8>>) andalso (Index =:= (erlang:length(
Raw_segments
)
- 1)) of
true ->
[];
false ->
[route_segment(Segment)]
end
end
),
lists:append(_pipe@1).
-file("src/proute/discover.gleam", 226).
-spec regular_route(binary(), proute@config:mount(), list(binary()), binary()) -> page_route().
regular_route(Source_file, Mount, Raw_segments, Page_module) ->
Route_segments = route_segments(Raw_segments),
Constructor = <<(erlang:element(9, Mount))/binary,
(constructor_name(Raw_segments))/binary>>,
{page_route,
route_kind(Route_segments),
Constructor,
route_path(erlang:element(4, Mount), Route_segments),
Route_segments,
dynamic_params(Route_segments),
Source_file,
Page_module}.
-file("src/proute/discover.gleam", 489).
-spec validate_raw_segments(binary(), list(binary())) -> {ok, nil} |
{error, discover_error()}.
validate_raw_segments(Source_file, Raw_segments) ->
_pipe = Raw_segments,
gleam@list:try_each(
_pipe,
fun(Segment) -> case proute@names:is_valid_module_segment(Segment) of
true ->
{ok, nil};
false ->
{error, {invalid_page_segment, Source_file, Segment}}
end end
).
-file("src/proute/discover.gleam", 246).
-spec not_found_route(binary(), proute@config:mount(), binary()) -> page_route().
not_found_route(Source_file, Mount, Page_module) ->
Segments = [{static_segment, <<"not_found"/utf8>>}],
{page_route,
not_found,
<<"NotFound"/utf8>>,
route_path(erlang:element(4, Mount), Segments),
Segments,
[],
Source_file,
Page_module}.
-file("src/proute/discover.gleam", 460).
-spec relative_source_path(binary(), binary()) -> binary().
relative_source_path(Source_file, Root) ->
Prefix = <<Root/binary, "/"/utf8>>,
case gleam_stdlib:string_starts_with(Source_file, Prefix) of
true ->
gleam@string:drop_start(Source_file, string:length(Prefix));
false ->
Source_file
end.
-file("src/proute/discover.gleam", 194).
-spec route_from_file(binary(), proute@config:mount()) -> {ok, page_route()} |
{error, discover_error()}.
route_from_file(Source_file, Mount) ->
Relative = relative_source_path(Source_file, erlang:element(3, Mount)),
Without_extension = drop_suffix(Relative, <<".gleam"/utf8>>),
Raw_segments = gleam@string:split(Without_extension, <<"/"/utf8>>),
case gleam@list:last(Raw_segments) of
{ok, <<"all_"/utf8>>} ->
{error, {unsupported_catch_all, Source_file}};
{ok, <<"not_found_"/utf8>>} ->
gleam@result:'try'(
validate_raw_segments(Source_file, Raw_segments),
fun(_) ->
gleam@result:'try'(
begin
_pipe = proute@names:module_path_from_file(
Source_file
),
gleam@result:map_error(
_pipe,
fun(_) ->
{invalid_page_module_path, Source_file}
end
)
end,
fun(Page_module) ->
{ok,
not_found_route(Source_file, Mount, Page_module)}
end
)
end
);
{ok, _} ->
gleam@result:'try'(
validate_raw_segments(Source_file, Raw_segments),
fun(_) ->
gleam@result:'try'(
begin
_pipe@1 = proute@names:module_path_from_file(
Source_file
),
gleam@result:map_error(
_pipe@1,
fun(_) ->
{invalid_page_module_path, Source_file}
end
)
end,
fun(Page_module@1) ->
Route = regular_route(
Source_file,
Mount,
Raw_segments,
Page_module@1
),
gleam@result:'try'(
validate_route(Source_file, Route),
fun(_) -> {ok, Route} end
)
end
)
end
);
{error, _} ->
{error, {pages_directory_unreadable, erlang:element(3, Mount)}}
end.
-file("src/proute/discover.gleam", 79).
-spec is_wire_module(binary()) -> boolean().
is_wire_module(Source_file) ->
gleam_stdlib:string_ends_with(Source_file, <<"/wire.gleam"/utf8>>).
-file("src/proute/discover.gleam", 483).
-spec join_path(list(binary())) -> binary().
join_path(Parts) ->
_pipe = Parts,
_pipe@1 = gleam@list:filter(_pipe, fun(Part) -> Part /= <<""/utf8>> end),
gleam@string:join(_pipe@1, <<"/"/utf8>>).
-file("src/proute/discover.gleam", 169).
-spec walk_entries(binary(), list(binary()), list(binary())) -> {ok,
list(binary())} |
{error, discover_error()}.
walk_entries(Directory, Entries, Files) ->
case Entries of
[] ->
{ok, lists:reverse(Files)};
[Entry | Rest] ->
Path = join_path([Directory, Entry]),
case simplifile_erl:is_directory(Path) of
{ok, true} ->
gleam@result:'try'(
walk_pages(Path),
fun(Nested) ->
walk_entries(
Directory,
Rest,
lists:append(lists:reverse(Nested), Files)
)
end
);
_ ->
walk_entries(Directory, Rest, [Path | Files])
end
end.
-file("src/proute/discover.gleam", 162).
-spec walk_pages(binary()) -> {ok, list(binary())} | {error, discover_error()}.
walk_pages(Root) ->
case simplifile_erl:read_directory(Root) of
{error, _} ->
{error, {pages_directory_unreadable, Root}};
{ok, Entries} ->
walk_entries(
Root,
begin
_pipe = Entries,
gleam@list:sort(_pipe, fun gleam@string:compare/2)
end,
[]
)
end.
-file("src/proute/discover.gleam", 59).
-spec discover_mount(proute@config:mount()) -> {ok, mount_routes()} |
{error, discover_error()}.
discover_mount(Mount) ->
gleam@result:'try'(
walk_pages(erlang:element(3, Mount)),
fun(Files) ->
gleam@result:'try'(
begin
_pipe = Files,
_pipe@1 = gleam@list:filter(
_pipe,
fun(_capture) ->
gleam_stdlib:string_ends_with(
_capture,
<<".gleam"/utf8>>
)
end
),
_pipe@2 = gleam@list:filter(
_pipe@1,
fun(File) -> not is_wire_module(File) end
),
gleam@list:try_map(
_pipe@2,
fun(_capture@1) ->
route_from_file(_capture@1, Mount)
end
)
end,
fun(Routes) ->
gleam@result:'try'(
require_not_found(Mount, Routes),
fun(_) ->
gleam@result:'try'(
reject_duplicate_paths(Routes, maps:new()),
fun(_) ->
gleam@result:'try'(
reject_duplicate_constructors(
Routes,
maps:new()
),
fun(_) ->
gleam@result:'try'(
reject_duplicate_helpers(
Routes,
maps:new()
),
fun(_) ->
{ok,
{mount_routes,
Mount,
sort_routes(Routes)}}
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/proute/discover.gleam", 83).
-spec describe_error(discover_error()) -> binary().
describe_error(Error) ->
case Error of
{pages_directory_unreadable, Path} ->
<<<<"Could not read pages directory "/utf8,
(gleam@string:inspect(Path))/binary>>/binary,
"."/utf8>>;
{missing_not_found, Mount_name, Pages} ->
<<<<<<<<"Mount "/utf8, (gleam@string:inspect(Mount_name))/binary>>/binary,
" at "/utf8>>/binary,
(gleam@string:inspect(Pages))/binary>>/binary,
" is missing not_found_.gleam. Add not_found_.gleam so proute can generate the NotFound route."/utf8>>;
{unsupported_catch_all, Source_file} ->
<<<<"Unsupported catch-all page "/utf8,
(gleam@string:inspect(Source_file))/binary>>/binary,
": all_.gleam is reserved but not generated yet."/utf8>>;
{invalid_page_module_path, Source_file@1} ->
<<<<"Invalid page module path "/utf8,
(gleam@string:inspect(Source_file@1))/binary>>/binary,
": page files must live under a src directory so generated imports can be module-relative."/utf8>>;
{invalid_page_segment, Source_file@2, Segment} ->
<<<<<<<<"Invalid page path segment "/utf8,
(gleam@string:inspect(Segment))/binary>>/binary,
" in "/utf8>>/binary,
(gleam@string:inspect(Source_file@2))/binary>>/binary,
": use valid Gleam module path segments."/utf8>>;
{invalid_route_param, Source_file@3, Param} ->
<<<<<<<<"Invalid route parameter "/utf8,
(gleam@string:inspect(Param))/binary>>/binary,
" in "/utf8>>/binary,
(gleam@string:inspect(Source_file@3))/binary>>/binary,
": dynamic route parameters must be valid Gleam labels."/utf8>>;
{duplicate_route_param, Source_file@4, Param@1} ->
<<<<<<<<"Duplicate route parameter "/utf8,
(gleam@string:inspect(Param@1))/binary>>/binary,
" in "/utf8>>/binary,
(gleam@string:inspect(Source_file@4))/binary>>/binary,
"."/utf8>>;
{invalid_constructor, Source_file@5, Constructor} ->
<<<<<<<<"Invalid route constructor "/utf8,
(gleam@string:inspect(Constructor))/binary>>/binary,
" from "/utf8>>/binary,
(gleam@string:inspect(Source_file@5))/binary>>/binary,
"."/utf8>>;
{duplicate_path, Path@1, First_file, Second_file} ->
<<<<<<<<<<<<"Duplicate route path "/utf8,
(gleam@string:inspect(Path@1))/binary>>/binary,
" from "/utf8>>/binary,
(gleam@string:inspect(First_file))/binary>>/binary,
" and "/utf8>>/binary,
(gleam@string:inspect(Second_file))/binary>>/binary,
"."/utf8>>;
{duplicate_constructor, Constructor@1, First_file@1, Second_file@1} ->
<<<<<<<<<<<<"Duplicate route constructor "/utf8,
(gleam@string:inspect(Constructor@1))/binary>>/binary,
" from "/utf8>>/binary,
(gleam@string:inspect(First_file@1))/binary>>/binary,
" and "/utf8>>/binary,
(gleam@string:inspect(Second_file@1))/binary>>/binary,
"."/utf8>>;
{duplicate_helper, Helper, First_file@2, Second_file@2} ->
<<<<<<<<<<<<"Duplicate route helper "/utf8,
(gleam@string:inspect(Helper))/binary>>/binary,
" from "/utf8>>/binary,
(gleam@string:inspect(First_file@2))/binary>>/binary,
" and "/utf8>>/binary,
(gleam@string:inspect(Second_file@2))/binary>>/binary,
"."/utf8>>
end.
-file("src/proute/discover.gleam", 476).
-spec drop_prefix(binary(), binary()) -> binary().
drop_prefix(Value, Prefix) ->
case gleam_stdlib:string_starts_with(Value, Prefix) of
true ->
gleam@string:drop_start(Value, string:length(Prefix));
false ->
Value
end.
-file("src/proute/discover.gleam", 152).
-spec page_module_path(binary()) -> binary().
page_module_path(Source_file) ->
_pipe = proute@names:module_path_from_file(Source_file),
gleam@result:unwrap(
_pipe,
begin
_pipe@1 = Source_file,
_pipe@2 = drop_suffix(_pipe@1, <<".gleam"/utf8>>),
_pipe@3 = gleam@string:replace(_pipe@2, <<"\\"/utf8>>, <<"/"/utf8>>),
drop_prefix(_pipe@3, <<"src/"/utf8>>)
end
).