-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}).