Current section
Files
Jump to
Current section
Files
src/glimit@rate_limiter.erl
-module(glimit@rate_limiter).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/glimit/rate_limiter.gleam").
-export([new/2, shutdown/1, set_now/2, hit/1, has_full_bucket/1]).
-export_type([state/0, message/0, hit_error/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(false).
-type state() :: {state,
integer(),
integer(),
float(),
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()}.
-type hit_error() :: rate_limited | unavailable.
-file("src/glimit/rate_limiter.gleam", 36).
?DOC(false).
-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} ->
gleam@int:max(0, Now@1 - Last_update)
end,
Tokens_to_add = erlang:float(erlang:element(3, State) * Time_diff) / 1000.0,
Token_count = begin
_pipe = (erlang:element(4, State) + Tokens_to_add),
_pipe@1 = gleam@float:min(_pipe, erlang:float(erlang:element(2, State))),
gleam@float:max(_pipe@1, +0.0)
end,
Last_update@1 = case Time_diff > 0 of
true ->
{some, Now@1};
false ->
case erlang:element(5, State) of
none ->
{some, Now@1};
{some, _} ->
erlang:element(5, State)
end
end,
{state,
erlang:element(2, State),
erlang:element(3, State),
Token_count,
Last_update@1,
erlang:element(6, State)}.
-file("src/glimit/rate_limiter.gleam", 64).
?DOC(false).
-spec remove_token(state()) -> state().
remove_token(State) ->
{state,
erlang:element(2, State),
erlang:element(3, State),
erlang:element(4, State) - 1.0,
erlang:element(5, State),
erlang:element(6, State)}.
-file("src/glimit/rate_limiter.gleam", 99).
?DOC(false).
-spec handle_message(state(), message()) -> gleam@otp@actor:next(state(), message()).
handle_message(State, Message) ->
case Message of
shutdown ->
gleam@otp@actor:stop();
{hit, Client} ->
State@1 = refill_bucket(State),
{Result, State@2} = case erlang:element(4, State@1) >= 1.0 of
false ->
{{error, nil}, State@1};
true ->
{{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:float(
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(
{state,
erlang:element(2, State),
erlang:element(3, State),
erlang:element(4, State),
erlang:element(5, State),
{some, Now}}
)
end.
-file("src/glimit/rate_limiter.gleam", 130).
?DOC(false).
-spec new(integer(), integer()) -> {ok, gleam@erlang@process:subject(message())} |
{error, nil}.
new(Max_token_count, Token_rate) ->
case (Max_token_count > 0) andalso (Token_rate > 0) of
false ->
{error, nil};
true ->
State = {state,
Max_token_count,
Token_rate,
erlang:float(Max_token_count),
none,
none},
_pipe = gleam@otp@actor:new(State),
_pipe@1 = gleam@otp@actor:on_message(_pipe, fun handle_message/2),
_pipe@2 = gleam@otp@actor:start(_pipe@1),
_pipe@3 = gleam@result:map(
_pipe@2,
fun(Started) -> erlang:element(3, Started) end
),
gleam@result:map_error(_pipe@3, fun(_) -> nil end)
end.
-file("src/glimit/rate_limiter.gleam", 156).
?DOC(false).
-spec shutdown(gleam@erlang@process:subject(message())) -> nil.
shutdown(Rate_limiter) ->
gleam@otp@actor:send(Rate_limiter, shutdown).
-file("src/glimit/rate_limiter.gleam", 180).
?DOC(false).
-spec set_now(gleam@erlang@process:subject(message()), integer()) -> nil.
set_now(Rate_limiter, Now) ->
gleam@otp@actor:send(Rate_limiter, {set_now, Now}).
-file("src/glimit/rate_limiter.gleam", 162).
?DOC(false).
-spec hit(gleam@erlang@process:subject(message())) -> {ok, nil} |
{error, hit_error()}.
hit(Rate_limiter) ->
case glimit@utils:safe_call(
Rate_limiter,
fun(Field@0) -> {hit, Field@0} end,
1000
) of
{ok, {ok, nil}} ->
{ok, nil};
{ok, {error, nil}} ->
{error, rate_limited};
{error, nil} ->
{error, unavailable}
end.
-file("src/glimit/rate_limiter.gleam", 172).
?DOC(false).
-spec has_full_bucket(gleam@erlang@process:subject(message())) -> boolean().
has_full_bucket(Rate_limiter) ->
_pipe = glimit@utils:safe_call(
Rate_limiter,
fun(Field@0) -> {has_full_bucket, Field@0} end,
1000
),
gleam@result:unwrap(_pipe, false).