-module(glimit). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([new/0, per_second/2, per_minute/2, per_hour/2, handler/2, identifier/2, try_build/1, build/1, apply/2, apply2/2, apply3/2, apply4/2]). -export_type([rate_limiter/3, rate_limiter_builder/3]). -type rate_limiter(HSK, HSL, HSM) :: {rate_limiter, gleam@erlang@process:subject(glimit@actor:message(HSM)), fun((HSK) -> HSL), fun((HSK) -> HSM)}. -type rate_limiter_builder(HSN, HSO, HSP) :: {rate_limiter_builder, gleam@option:option(integer()), gleam@option:option(integer()), gleam@option:option(integer()), gleam@option:option(fun((HSN) -> HSP)), gleam@option:option(fun((HSN) -> HSO))}. -spec new() -> rate_limiter_builder(any(), any(), any()). new() -> {rate_limiter_builder, none, none, none, none, none}. -spec per_second(rate_limiter_builder(HSW, HSX, HSY), integer()) -> rate_limiter_builder(HSW, HSX, HSY). per_second(Limiter, Limit) -> erlang:setelement(2, Limiter, {some, Limit}). -spec per_minute(rate_limiter_builder(HTF, HTG, HTH), integer()) -> rate_limiter_builder(HTF, HTG, HTH). per_minute(Limiter, Limit) -> erlang:setelement(3, Limiter, {some, Limit}). -spec per_hour(rate_limiter_builder(HTO, HTP, HTQ), integer()) -> rate_limiter_builder(HTO, HTP, HTQ). per_hour(Limiter, Limit) -> erlang:setelement(4, Limiter, {some, Limit}). -spec handler(rate_limiter_builder(HTX, HTY, HTZ), fun((HTX) -> HTY)) -> rate_limiter_builder(HTX, HTY, HTZ). handler(Limiter, Handler) -> erlang:setelement(6, Limiter, {some, Handler}). -spec identifier(rate_limiter_builder(HUG, HUH, HUI), fun((HUG) -> HUI)) -> rate_limiter_builder(HUG, HUH, HUI). identifier(Limiter, Identifier) -> erlang:setelement(5, Limiter, {some, Identifier}). -spec try_build(rate_limiter_builder(HUY, HUZ, HVA)) -> {ok, rate_limiter(HUY, HUZ, HVA)} | {error, binary()}. try_build(Config) -> gleam@result:'try'( begin _pipe = glimit@actor:new( erlang:element(2, Config), erlang:element(3, Config), erlang:element(4, Config) ), gleam@result:map_error( _pipe, fun(_) -> <<"Failed to start rate limiter actor"/utf8>> end ) end, fun(Subject) -> gleam@result:'try'(case erlang:element(5, Config) of {some, Identifier} -> {ok, Identifier}; none -> {error, <<"Identifier function is required"/utf8>>} end, fun(Identifier@1) -> gleam@result:'try'(case erlang:element(6, Config) of {some, Handler} -> {ok, Handler}; none -> {error, <<"Handler function is required"/utf8>>} end, fun(Handler@1) -> {ok, {rate_limiter, Subject, Handler@1, Identifier@1}} end) end) end ). -spec build(rate_limiter_builder(HUP, HUQ, HUR)) -> rate_limiter(HUP, HUQ, HUR). build(Config) -> case try_build(Config) of {ok, Limiter} -> Limiter; {error, Message} -> erlang:error(#{gleam_error => panic, message => Message, module => <<"glimit"/utf8>>, function => <<"build"/utf8>>, line => 130}) end. -spec apply(fun((HVJ) -> HVK), rate_limiter(HVJ, HVK, any())) -> fun((HVJ) -> HVK). apply(Func, Limiter) -> fun(Input) -> Identifier = (erlang:element(4, Limiter))(Input), case glimit@actor:hit(erlang:element(2, Limiter), Identifier) of {ok, nil} -> Func(Input); {error, nil} -> (erlang:element(3, Limiter))(Input) end end. -spec apply2(fun((HVP, HVQ) -> HVR), rate_limiter({HVP, HVQ}, HVR, any())) -> fun((HVP, HVQ) -> HVR). apply2(Func, Limiter) -> fun(A, B) -> Identifier = (erlang:element(4, Limiter))({A, B}), case glimit@actor:hit(erlang:element(2, Limiter), Identifier) of {ok, nil} -> Func(A, B); {error, nil} -> (erlang:element(3, Limiter))({A, B}) end end. -spec apply3( fun((HVW, HVX, HVY) -> HVZ), rate_limiter({HVW, HVX, HVY}, HVZ, any()) ) -> fun((HVW, HVX, HVY) -> HVZ). apply3(Func, Limiter) -> fun(A, B, C) -> Identifier = (erlang:element(4, Limiter))({A, B, C}), case glimit@actor:hit(erlang:element(2, Limiter), Identifier) of {ok, nil} -> Func(A, B, C); {error, nil} -> (erlang:element(3, Limiter))({A, B, C}) end end. -spec apply4( fun((HWE, HWF, HWG, HWH) -> HWI), rate_limiter({HWE, HWF, HWG, HWH}, HWI, any()) ) -> fun((HWE, HWF, HWG, HWH) -> HWI). apply4(Func, Limiter) -> fun(A, B, C, D) -> Identifier = (erlang:element(4, Limiter))({A, B, C, D}), case glimit@actor:hit(erlang:element(2, Limiter), Identifier) of {ok, nil} -> Func(A, B, C, D); {error, nil} -> (erlang:element(3, Limiter))({A, B, C, D}) end end.