-module(glimiter). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([new_limiter/2, limit_guard/4]). -export_type([limiter/0]). -opaque limiter() :: {limiter, gleam@dict:dict(binary(), list(birl:time())), integer(), birl@duration:duration()}. -spec new_limiter(integer(), birl@duration:duration()) -> limiter(). new_limiter(Count, Size) -> {limiter, gleam@dict:new(), Count, Size}. -spec add(limiter(), binary()) -> gleam@dict:dict(binary(), list(birl:time())). add(Limiter, Key) -> Now = birl:now(), case gleam@dict:get(erlang:element(2, Limiter), Key) of {ok, Timestamps} -> gleam@dict:insert( erlang:element(2, Limiter), Key, [Now | Timestamps] ); {error, _} -> gleam@dict:insert(erlang:element(2, Limiter), Key, [Now]) end. -spec filter_prev_window(limiter(), binary()) -> gleam@dict:dict(binary(), list(birl:time())). filter_prev_window(Limiter, Key) -> _assert_subject = gleam@dict:get(erlang:element(2, Limiter), Key), {ok, Times} = case _assert_subject of {ok, _} -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"glimiter"/utf8>>, function => <<"filter_prev_window"/utf8>>, line => 47}) end, Now = birl:now(), New_times = gleam@list:filter( Times, fun(Time) -> End_time = birl:add(Time, erlang:element(4, Limiter)), case birl:compare(Now, End_time) of lt -> true; eq -> true; _ -> false end end ), gleam@dict:insert(erlang:element(2, Limiter), Key, New_times). -spec limit_guard(limiter(), binary(), HFM, fun((limiter()) -> HFM)) -> HFM. limit_guard(Limiter, Key, Consequence, Alternetive) -> Limiter@1 = {limiter, add(Limiter, Key), erlang:element(3, Limiter), erlang:element(4, Limiter)}, Limiter@2 = {limiter, filter_prev_window(Limiter@1, Key), erlang:element(3, Limiter@1), erlang:element(4, Limiter@1)}, _assert_subject = gleam@dict:get(erlang:element(2, Limiter@2), Key), {ok, Times} = case _assert_subject of {ok, _} -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"glimiter"/utf8>>, function => <<"limit_guard"/utf8>>, line => 25}) end, case erlang:length(Times) < erlang:element(3, Limiter@2) of true -> Alternetive(Limiter@2); false -> Consequence end.