-module(glimit@actor). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([new/3, hit/2, stop/1]). -export_type([message/1, state/3]). -type message(HNG) :: shutdown | {hit, HNG, gleam@erlang@process:subject({ok, nil} | {error, nil})}. -type state(HNH, HNI, HNJ) :: {rate_limiter_state, gleam@dict:dict(HNJ, list(integer())), gleam@option:option(integer()), gleam@option:option(integer()), gleam@option:option(integer())} | {gleam_phantom, HNH, HNI}. -spec handle_message(message(HNK), state(HNM, HNN, HNK)) -> gleam@otp@actor:next(message(HNK), state(HNM, HNN, HNK)). handle_message(Message, State) -> case Message of shutdown -> {stop, normal}; {hit, Identifier, Client} -> Timestamp = glimit@utils:now(), Hits = begin _pipe = erlang:element(2, State), _pipe@1 = gleam@dict:get(_pipe, Identifier), _pipe@2 = gleam@result:unwrap(_pipe@1, []), _pipe@3 = gleam@list:filter( _pipe@2, fun(Hit) -> Hit >= (Timestamp - (60 * 60)) end ), lists:append(_pipe@3, [Timestamp]) end, Hit_log = begin _pipe@4 = erlang:element(2, State), gleam@dict:insert(_pipe@4, Identifier, Hits) end, State@1 = erlang:setelement(2, State, Hit_log), Hits_last_hour = begin _pipe@5 = Hits, erlang:length(_pipe@5) end, Hits_last_minute = begin _pipe@6 = Hits, _pipe@7 = gleam@list:filter( _pipe@6, fun(Hit@1) -> Hit@1 >= (Timestamp - 60) end ), erlang:length(_pipe@7) end, Hits_last_second = begin _pipe@8 = Hits, _pipe@9 = gleam@list:filter( _pipe@8, fun(Hit@2) -> Hit@2 >= (Timestamp - 1) end ), erlang:length(_pipe@9) end, Limit_reached = ((case erlang:element(5, State@1) of {some, Limit} -> Hits_last_hour > Limit; none -> false end orelse case erlang:element(4, State@1) of {some, Limit@1} -> Hits_last_minute > Limit@1; none -> false end) orelse case erlang:element(3, State@1) of {some, Limit@2} -> Hits_last_second > Limit@2; none -> false end), case Limit_reached of true -> gleam@erlang@process:send(Client, {error, nil}); false -> gleam@erlang@process:send(Client, {ok, nil}) end, gleam@otp@actor:continue(State@1) end. -spec new( gleam@option:option(integer()), gleam@option:option(integer()), gleam@option:option(integer()) ) -> {ok, gleam@erlang@process:subject(message(any()))} | {error, nil}. new(Per_second, Per_minute, Per_hour) -> State = {rate_limiter_state, gleam@dict:new(), Per_second, Per_minute, Per_hour}, _pipe = gleam@otp@actor:start(State, fun handle_message/2), gleam@result:nil_error(_pipe). -spec hit(gleam@erlang@process:subject(message(HOF)), HOF) -> {ok, nil} | {error, nil}. hit(Subject, Identifier) -> gleam@otp@actor:call( Subject, fun(_capture) -> {hit, Identifier, _capture} end, 10 ). -spec stop(gleam@erlang@process:subject(message(any()))) -> nil. stop(Subject) -> gleam@otp@actor:send(Subject, shutdown).