-module(proute@config). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/proute/config.gleam"). -export([default_page_shared_state_type/1, parse/1, describe_error/1]). -export_type([config/0, mount/0, raw_config/0, raw_mount/0, config_error/0]). -type config() :: {config, binary(), binary(), list(mount())}. -type mount() :: {mount, binary(), binary(), binary(), binary(), binary(), binary(), binary(), binary(), binary()}. -type raw_config() :: {raw_config, binary(), binary(), list(raw_mount())}. -type raw_mount() :: {raw_mount, binary(), {ok, binary()} | {error, nil}, {ok, binary()} | {error, nil}, {ok, binary()} | {error, nil}, {ok, binary()} | {error, nil}}. -type config_error() :: {missing_config, binary()} | {malformed_config, binary()} | missing_proute_table | missing_mounts | {missing_mount_name, integer()} | {invalid_mount_name, binary()} | {duplicate_mount_name, binary()} | {invalid_constructor_prefix, binary(), binary()} | {invalid_route_root, binary(), binary()} | {invalid_page_shared_state_type, binary(), binary()}. -file("src/proute/config.gleam", 352). -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/config.gleam", 338). -spec validate_constructor_prefix(binary(), binary()) -> {ok, nil} | {error, config_error()}. validate_constructor_prefix(Mount_name, Constructor_prefix) -> case Constructor_prefix of <<""/utf8>> -> {ok, nil}; _ -> case proute@names:is_valid_type_name( <> ) of true -> {ok, nil}; false -> {error, {invalid_constructor_prefix, Mount_name, Constructor_prefix}} end end. -file("src/proute/config.gleam", 334). -spec default_page_shared_state_type(binary()) -> binary(). default_page_shared_state_type(Name) -> <<<<<>/binary, (proute@names:pascal_case(Name))/binary>>/binary, "PageSharedState"/utf8>>. -file("src/proute/config.gleam", 327). -spec default_constructor_prefix(binary(), binary()) -> binary(). default_constructor_prefix(Name, Route_root) -> case Route_root of <<"/"/utf8>> -> <<""/utf8>>; _ -> proute@names:pascal_case(Name) end. -file("src/proute/config.gleam", 315). -spec is_valid_route_root(binary()) -> boolean(). is_valid_route_root(Route_root) -> (Route_root =:= <<"/"/utf8>>) orelse (((((gleam_stdlib:string_starts_with( Route_root, <<"/"/utf8>> ) andalso not gleam_stdlib:string_ends_with(Route_root, <<"/"/utf8>>)) andalso not gleam_stdlib:contains_string(Route_root, <<"//"/utf8>>)) andalso not gleam_stdlib:contains_string(Route_root, <<"?"/utf8>>)) andalso not gleam_stdlib:contains_string(Route_root, <<"#"/utf8>>)) andalso not gleam_stdlib:contains_string(Route_root, <<" "/utf8>>)). -file("src/proute/config.gleam", 303). -spec normalize_route_root(binary()) -> binary(). normalize_route_root(Route_root) -> Pieces = begin _pipe = Route_root, _pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>), gleam@list:filter(_pipe@1, fun(Piece) -> Piece /= <<""/utf8>> end) end, case Pieces of [] -> <<"/"/utf8>>; _ -> <<"/"/utf8, (gleam@string:join(Pieces, <<"/"/utf8>>))/binary>> end. -file("src/proute/config.gleam", 293). -spec resolve_route_root(raw_mount()) -> {ok, binary()} | {error, config_error()}. resolve_route_root(Mount) -> Root = gleam@result:unwrap( erlang:element(4, Mount), <<"/"/utf8, (erlang:element(2, Mount))/binary>> ), Normalized = normalize_route_root(Root), case is_valid_route_root(Normalized) of true -> {ok, Normalized}; false -> {error, {invalid_route_root, erlang:element(2, Mount), Root}} end. -file("src/proute/config.gleam", 225). -spec resolve_mount(raw_mount(), binary(), binary()) -> {ok, mount()} | {error, config_error()}. resolve_mount(Mount, Pages_root, Output_root) -> gleam@result:'try'( resolve_route_root(Mount), fun(Route_root) -> Pages = gleam@result:unwrap( erlang:element(3, Mount), join_path( [Pages_root, erlang:element(2, Mount), <<"pages"/utf8>>] ) ), Output_dir = join_path([Output_root, erlang:element(2, Mount)]), Constructor_prefix = gleam@result:unwrap( erlang:element(5, Mount), default_constructor_prefix(erlang:element(2, Mount), Route_root) ), Page_shared_state_type = gleam@result:unwrap( erlang:element(6, Mount), default_page_shared_state_type(erlang:element(2, Mount)) ), gleam@result:'try'( validate_constructor_prefix( erlang:element(2, Mount), Constructor_prefix ), fun(_) -> gleam@result:'try'( begin _pipe = proute@names:parse_type_reference( Page_shared_state_type ), gleam@result:map_error( _pipe, fun(_) -> {invalid_page_shared_state_type, erlang:element(2, Mount), Page_shared_state_type} end ) end, fun(_) -> {ok, {mount, erlang:element(2, Mount), Pages, Route_root, Output_dir, join_path( [Output_dir, <<"routes.gleam"/utf8>>] ), join_path( [Output_dir, <<"pages.gleam"/utf8>>] ), join_path( [Output_dir, <<"page_input.gleam"/utf8>>] ), Constructor_prefix, Page_shared_state_type}} end ) end ) end ). -file("src/proute/config.gleam", 289). -spec is_valid_mount_name(binary()) -> boolean(). is_valid_mount_name(Name) -> proute@names:is_valid_module_path(Name). -file("src/proute/config.gleam", 269). -spec validate_mount_names(list(raw_mount()), gleam@dict:dict(binary(), nil)) -> {ok, nil} | {error, config_error()}. validate_mount_names(Mounts, Seen) -> case Mounts of [] -> {ok, nil}; [Mount | Rest] -> case is_valid_mount_name(erlang:element(2, Mount)) of false -> {error, {invalid_mount_name, erlang:element(2, Mount)}}; true -> case gleam@dict:has_key(Seen, erlang:element(2, Mount)) of true -> {error, {duplicate_mount_name, erlang:element(2, Mount)}}; false -> validate_mount_names( Rest, gleam@dict:insert( Seen, erlang:element(2, Mount), nil ) ) end end end. -file("src/proute/config.gleam", 211). -spec resolve(raw_config()) -> {ok, config()} | {error, config_error()}. resolve(Raw) -> gleam@result:'try'( validate_mount_names(erlang:element(4, Raw), maps:new()), fun(_) -> _pipe = erlang:element(4, Raw), _pipe@1 = gleam@list:try_map( _pipe, fun(_capture) -> resolve_mount( _capture, erlang:element(2, Raw), erlang:element(3, Raw) ) end ), gleam@result:map( _pipe@1, fun(Mounts) -> {config, erlang:element(2, Raw), erlang:element(3, Raw), Mounts} end ) end ). -file("src/proute/config.gleam", 415). -spec describe_tom_error(tom:get_error()) -> binary(). describe_tom_error(Error) -> case Error of {not_found, Path} -> <<"missing "/utf8, (gleam@string:join(Path, <<"."/utf8>>))/binary>>; {wrong_type, _, Expected, Got} -> <<<<<<"expected "/utf8, Expected/binary>>/binary, ", got "/utf8>>/binary, Got/binary>> end. -file("src/proute/config.gleam", 388). -spec optional_string_result( gleam@dict:dict(binary(), tom:toml()), binary(), list(binary()) ) -> {ok, {ok, binary()} | {error, nil}} | {error, config_error()}. optional_string_result(Table, Field, Path) -> case tom:get_string(Table, [Field]) of {ok, Value} -> {ok, {ok, Value}}; {error, {not_found, _}} -> {ok, {error, nil}}; {error, Error} -> {error, {malformed_config, <<<<<<"Invalid "/utf8, (gleam@string:join(Path, <<"."/utf8>>))/binary>>/binary, ": "/utf8>>/binary, (describe_tom_error(Error))/binary>>}} end. -file("src/proute/config.gleam", 358). -spec optional_string( gleam@dict:dict(binary(), tom:toml()), binary(), list(binary()), binary() ) -> {ok, binary()} | {error, config_error()}. optional_string(Table, Field, Path, Default) -> case optional_string_result(Table, Field, Path) of {ok, {ok, Value}} -> {ok, Value}; {ok, {error, _}} -> {ok, Default}; {error, Error} -> {error, Error} end. -file("src/proute/config.gleam", 371). -spec required_mount_name(gleam@dict:dict(binary(), tom:toml()), integer()) -> {ok, binary()} | {error, config_error()}. required_mount_name(Table, Index) -> case tom:get_string(Table, [<<"name"/utf8>>]) of {ok, Value} -> {ok, Value}; {error, {not_found, _}} -> {error, {missing_mount_name, Index}}; {error, Error} -> {error, {malformed_config, <<<<<<"Invalid proute.mounts."/utf8, (erlang:integer_to_binary(Index))/binary>>/binary, ".name: "/utf8>>/binary, (describe_tom_error(Error))/binary>>}} end. -file("src/proute/config.gleam", 155). -spec read_mounts_loop( list(gleam@dict:dict(binary(), tom:toml())), integer(), list(raw_mount()) ) -> {ok, list(raw_mount())} | {error, config_error()}. read_mounts_loop(Mounts, Index, Read) -> case Mounts of [] -> {ok, lists:reverse(Read)}; [Mount | Rest] -> gleam@result:'try'( required_mount_name(Mount, Index), fun(Name) -> gleam@result:'try'( optional_string_result( Mount, <<"pages"/utf8>>, [<<"proute"/utf8>>, <<"mounts"/utf8>>, erlang:integer_to_binary(Index), <<"pages"/utf8>>] ), fun(Pages) -> gleam@result:'try'( optional_string_result( Mount, <<"route_root"/utf8>>, [<<"proute"/utf8>>, <<"mounts"/utf8>>, erlang:integer_to_binary(Index), <<"route_root"/utf8>>] ), fun(Route_root) -> gleam@result:'try'( optional_string_result( Mount, <<"constructor_prefix"/utf8>>, [<<"proute"/utf8>>, <<"mounts"/utf8>>, erlang:integer_to_binary(Index), <<"constructor_prefix"/utf8>>] ), fun(Constructor_prefix) -> gleam@result:'try'( optional_string_result( Mount, <<"page_shared_state_type"/utf8>>, [<<"proute"/utf8>>, <<"mounts"/utf8>>, erlang:integer_to_binary( Index ), <<"page_shared_state_type"/utf8>>] ), fun(Page_shared_state_type) -> read_mounts_loop( Rest, Index + 1, [{raw_mount, Name, Pages, Route_root, Constructor_prefix, Page_shared_state_type} | Read] ) end ) end ) end ) end ) end ) end. -file("src/proute/config.gleam", 135). -spec read_mounts(gleam@dict:dict(binary(), tom:toml())) -> {ok, list(raw_mount())} | {error, config_error()}. read_mounts(Proute) -> case tom:get_array(Proute, [<<"mounts"/utf8>>]) of {error, {not_found, _}} -> {error, missing_mounts}; {error, Error} -> {error, {malformed_config, <<"Invalid proute.mounts: "/utf8, (describe_tom_error(Error))/binary>>}}; {ok, []} -> {error, missing_mounts}; {ok, Mounts} -> case gleam@list:try_map(Mounts, fun tom:as_table/1) of {ok, Mounts@1} -> _pipe = Mounts@1, read_mounts_loop(_pipe, 1, []); {error, Error@1} -> {error, {malformed_config, <<"Invalid proute.mounts: expected array of tables, "/utf8, (describe_tom_error(Error@1))/binary>>}} end end. -file("src/proute/config.gleam", 406). -spec read_proute_table(gleam@dict:dict(binary(), tom:toml())) -> {ok, gleam@dict:dict(binary(), tom:toml())} | {error, config_error()}. read_proute_table(Table) -> case tom:get_table(Table, [<<"proute"/utf8>>]) of {ok, Value} -> {ok, Value}; {error, {not_found, _}} -> {error, missing_proute_table}; {error, Error} -> {error, {malformed_config, <<"Invalid proute: "/utf8, (describe_tom_error(Error))/binary>>}} end. -file("src/proute/config.gleam", 115). -spec read_raw_config(gleam@dict:dict(binary(), tom:toml())) -> {ok, raw_config()} | {error, config_error()}. read_raw_config(Document) -> gleam@result:'try'( read_proute_table(Document), fun(Proute) -> gleam@result:'try'( read_mounts(Proute), fun(Mounts) -> gleam@result:'try'( optional_string( Proute, <<"pages_root"/utf8>>, [<<"proute"/utf8>>, <<"pages_root"/utf8>>], <<"src"/utf8>> ), fun(Pages_root) -> gleam@result:'try'( optional_string( Proute, <<"output_root"/utf8>>, [<<"proute"/utf8>>, <<"output_root"/utf8>>], <<"src/generated/proute"/utf8>> ), fun(Output_root) -> {ok, {raw_config, Pages_root, Output_root, Mounts}} end ) end ) end ) end ). -file("src/proute/config.gleam", 58). -spec parse(binary()) -> {ok, config()} | {error, config_error()}. parse(Source) -> gleam@result:'try'( begin _pipe = tom:parse(Source), gleam@result:map_error( _pipe, fun(Error) -> {malformed_config, <<"Could not parse proute.toml: "/utf8, (gleam@string:inspect(Error))/binary>>} end ) end, fun(Document) -> gleam@result:'try'( read_raw_config(Document), fun(Raw) -> resolve(Raw) end ) end ). -file("src/proute/config.gleam", 70). -spec describe_error(config_error()) -> binary(). describe_error(Error) -> case Error of {missing_config, Path} -> <<<<"Missing proute config: expected "/utf8, Path/binary>>/binary, " in the current directory."/utf8>>; {malformed_config, Message} -> Message; missing_proute_table -> <<"Missing [proute] config table in proute.toml."/utf8>>; missing_mounts -> <<"Missing [[proute.mounts]] entries in proute.toml."/utf8>>; {missing_mount_name, Index} -> <<<<"Missing required name for [[proute.mounts]] entry #"/utf8, (erlang:integer_to_binary(Index))/binary>>/binary, "."/utf8>>; {invalid_mount_name, Name} -> <<<<"Invalid mount name "/utf8, (gleam@string:inspect(Name))/binary>>/binary, ": use a valid Gleam module path segment."/utf8>>; {duplicate_mount_name, Name@1} -> <<<<"Duplicate mount name "/utf8, (gleam@string:inspect(Name@1))/binary>>/binary, " in proute.toml."/utf8>>; {invalid_constructor_prefix, Name@2, Constructor_prefix} -> <<<<<<<<<<<<"Invalid constructor_prefix "/utf8, (gleam@string:inspect(Constructor_prefix))/binary>>/binary, " for mount "/utf8>>/binary, (gleam@string:inspect(Name@2))/binary>>/binary, ": it must produce valid Gleam route constructors, such as "/utf8>>/binary, (gleam@string:inspect( <> ))/binary>>/binary, "."/utf8>>; {invalid_route_root, Name@3, Route_root} -> <<<<<<<<"Invalid route_root "/utf8, (gleam@string:inspect(Route_root))/binary>>/binary, " for mount "/utf8>>/binary, (gleam@string:inspect(Name@3))/binary>>/binary, ": route roots must normalize to / or an absolute path without empty segments."/utf8>>; {invalid_page_shared_state_type, Name@4, Value} -> <<<<<<<<<<<<"Invalid page_shared_state_type "/utf8, (gleam@string:inspect(Value))/binary>>/binary, " for mount "/utf8>>/binary, (gleam@string:inspect(Name@4))/binary>>/binary, ": use a Gleam module path and type name, such as "/utf8>>/binary, (gleam@string:inspect( default_page_shared_state_type(Name@4) ))/binary>>/binary, "."/utf8>> end.