Current section

Files

Jump to
glimit src glimit@rate_limiter.erl
Raw

src/glimit@rate_limiter.erl

-module(glimit@rate_limiter).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([new/2, shutdown/1, hit/1, has_full_bucket/1, set_now/2]).
-export_type([state/0, message/0]).
-type state() :: {state,
integer(),
integer(),
integer(),
gleam@option:option(integer()),
gleam@option:option(integer())}.
-type message() :: shutdown |
{hit, gleam@erlang@process:subject({ok, nil} | {error, nil})} |
{has_full_bucket, gleam@erlang@process:subject(boolean())} |
{set_now, integer()}.
-spec refill_bucket(state()) -> state().
refill_bucket(State) ->
Now@1 = case erlang:element(6, State) of
none ->
glimit@utils:now();
{some, Now} ->
Now
end,
Time_diff = case erlang:element(5, State) of
none ->
0;
{some, Last_update} ->
Now@1 - Last_update
end,
Token_count = begin
_pipe = erlang:element(4, State) + (erlang:element(3, State) * Time_diff),
_pipe@1 = gleam@int:min(_pipe, erlang:element(2, State)),
gleam@int:max(_pipe@1, 0)
end,
erlang:setelement(
5,
erlang:setelement(4, State, Token_count),
{some, Now@1}
).
-spec remove_token(state()) -> state().
remove_token(State) ->
erlang:setelement(4, State, erlang:element(4, State) - 1).
-spec handle_message(message(), state()) -> gleam@otp@actor:next(message(), state()).
handle_message(Message, State) ->
case Message of
shutdown ->
{stop, normal};
{hit, Client} ->
State@1 = refill_bucket(State),
{Result, State@2} = case erlang:element(4, State@1) of
0 ->
{{error, nil}, State@1};
_ ->
{{ok, nil}, remove_token(State@1)}
end,
gleam@otp@actor:send(Client, Result),
gleam@otp@actor:continue(State@2);
{has_full_bucket, Client@1} ->
State@3 = refill_bucket(State),
Result@1 = erlang:element(4, State@3) =:= erlang:element(2, State@3),
gleam@otp@actor:send(Client@1, Result@1),
gleam@otp@actor:continue(State@3);
{set_now, Now} ->
gleam@otp@actor:continue(erlang:setelement(6, State, {some, Now}))
end.
-spec new(integer(), integer()) -> {ok, gleam@erlang@process:subject(message())} |
{error, nil}.
new(Max_token_count, Token_rate) ->
State = {state, Max_token_count, Token_rate, Max_token_count, none, none},
_pipe = gleam@otp@actor:start(State, fun handle_message/2),
gleam@result:nil_error(_pipe).
-spec shutdown(gleam@erlang@process:subject(message())) -> nil.
shutdown(Rate_limiter) ->
gleam@otp@actor:send(Rate_limiter, shutdown).
-spec hit(gleam@erlang@process:subject(message())) -> {ok, nil} | {error, nil}.
hit(Rate_limiter) ->
gleam@otp@actor:call(Rate_limiter, fun(Field@0) -> {hit, Field@0} end, 10).
-spec has_full_bucket(gleam@erlang@process:subject(message())) -> boolean().
has_full_bucket(Rate_limiter) ->
gleam@otp@actor:call(
Rate_limiter,
fun(Field@0) -> {has_full_bucket, Field@0} end,
10
).
-spec set_now(gleam@erlang@process:subject(message()), integer()) -> nil.
set_now(Rate_limiter, Now) ->
gleam@otp@actor:send(Rate_limiter, {set_now, Now}).