src/lightspeed@transport@contract.erl

-module(lightspeed@transport@contract).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lightspeed/transport/contract.gleam").
-export([authenticate/2, allow_all/1, deny_all/1, protect/2, allow_protection/0, deny_protection/1, require_csrf/1, limit_rate/2, allow_rate_limit/0, deny_rate_limit/2, error_to_string/1]).
-export_type([auth_context/0, auth_result/0, adapter_error/0, auth_hook/0, protection_context/0, protection_result/0, protection_hook/0, rate_limit_context/0, rate_limit_result/0, rate_limit_hook/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(" Transport adapter contract primitives.\n").

-type auth_context() :: {auth_context, binary(), binary(), binary(), binary()}.

-type auth_result() :: {authorized, binary()} | {denied, binary()}.

-type adapter_error() :: {authentication_failed, binary()} |
    {protection_rejected, binary()} |
    {rate_limited, binary(), integer()} |
    {protocol_decode_failed, lightspeed@protocol:decode_error()} |
    {unsupported_client_frame, binary()} |
    {unsupported_client_event, binary()} |
    {invalid_adapter_state, binary()}.

-type auth_hook() :: {auth_hook, fun((auth_context()) -> auth_result())}.

-type protection_context() :: {protection_context,
        binary(),
        binary(),
        binary(),
        binary(),
        binary()}.

-type protection_result() :: protected | {rejected, binary()}.

-type protection_hook() :: {protection_hook,
        fun((protection_context()) -> protection_result())}.

-type rate_limit_context() :: {rate_limit_context,
        binary(),
        binary(),
        binary(),
        integer()}.

-type rate_limit_result() :: rate_allowed | {limited, binary(), integer()}.

-type rate_limit_hook() :: {rate_limit_hook,
        fun((rate_limit_context()) -> rate_limit_result())}.

-file("src/lightspeed/transport/contract.gleam", 82).
?DOC(" Execute an auth hook.\n").
-spec authenticate(auth_hook(), auth_context()) -> auth_result().
authenticate(Hook, Context) ->
    (erlang:element(2, Hook))(Context).

-file("src/lightspeed/transport/contract.gleam", 87).
?DOC(" Accept every connection with the given owner id.\n").
-spec allow_all(binary()) -> auth_hook().
allow_all(Owner) ->
    {auth_hook, fun(_) -> {authorized, Owner} end}.

-file("src/lightspeed/transport/contract.gleam", 92).
?DOC(" Reject every connection with a fixed reason.\n").
-spec deny_all(binary()) -> auth_hook().
deny_all(Reason) ->
    {auth_hook, fun(_) -> {denied, Reason} end}.

-file("src/lightspeed/transport/contract.gleam", 97).
?DOC(" Execute a protection hook.\n").
-spec protect(protection_hook(), protection_context()) -> protection_result().
protect(Hook, Context) ->
    (erlang:element(2, Hook))(Context).

-file("src/lightspeed/transport/contract.gleam", 105).
?DOC(" Allow every connection through protection checks.\n").
-spec allow_protection() -> protection_hook().
allow_protection() ->
    {protection_hook, fun(_) -> protected end}.

-file("src/lightspeed/transport/contract.gleam", 110).
?DOC(" Deny every connection through protection checks.\n").
-spec deny_protection(binary()) -> protection_hook().
deny_protection(Reason) ->
    {protection_hook, fun(_) -> {rejected, Reason} end}.

-file("src/lightspeed/transport/contract.gleam", 115).
?DOC(" Require an exact CSRF token match.\n").
-spec require_csrf(binary()) -> protection_hook().
require_csrf(Expected_token) ->
    {protection_hook,
        fun(Context) -> case erlang:element(5, Context) =:= Expected_token of
                true ->
                    protected;

                false ->
                    {rejected, <<"csrf_token_mismatch"/utf8>>}
            end end}.

-file("src/lightspeed/transport/contract.gleam", 125).
?DOC(" Execute a rate-limit hook.\n").
-spec limit_rate(rate_limit_hook(), rate_limit_context()) -> rate_limit_result().
limit_rate(Hook, Context) ->
    (erlang:element(2, Hook))(Context).

-file("src/lightspeed/transport/contract.gleam", 133).
?DOC(" Allow every event by rate-limit policy.\n").
-spec allow_rate_limit() -> rate_limit_hook().
allow_rate_limit() ->
    {rate_limit_hook, fun(_) -> rate_allowed end}.

-file("src/lightspeed/transport/contract.gleam", 138).
?DOC(" Deny every event by rate-limit policy.\n").
-spec deny_rate_limit(binary(), integer()) -> rate_limit_hook().
deny_rate_limit(Reason, Retry_after_ms) ->
    {rate_limit_hook, fun(_) -> {limited, Reason, Retry_after_ms} end}.

-file("src/lightspeed/transport/contract.gleam", 145).
?DOC(" Stable adapter error string for logs and tests.\n").
-spec error_to_string(adapter_error()) -> binary().
error_to_string(Error) ->
    case Error of
        {authentication_failed, Reason} ->
            <<"authentication_failed:"/utf8, Reason/binary>>;

        {protection_rejected, Reason@1} ->
            <<"protection_rejected:"/utf8, Reason@1/binary>>;

        {rate_limited, Reason@2, Retry_after_ms} ->
            <<<<<<"rate_limited:"/utf8, Reason@2/binary>>/binary, ":"/utf8>>/binary,
                (erlang:integer_to_binary(Retry_after_ms))/binary>>;

        {protocol_decode_failed, Decode_error} ->
            <<"protocol_decode_failed:"/utf8,
                (lightspeed@protocol:decode_error_to_string(Decode_error))/binary>>;

        {unsupported_client_frame, Frame} ->
            <<"unsupported_client_frame:"/utf8, Frame/binary>>;

        {unsupported_client_event, Event} ->
            <<"unsupported_client_event:"/utf8, Event/binary>>;

        {invalid_adapter_state, Reason@3} ->
            <<"invalid_adapter_state:"/utf8, Reason@3/binary>>
    end.