-module(aws@waiter).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aws/waiter.gleam").
-export([wait/4]).
-export_type([step/1, waiter_error/1]).
-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(
" Polling helper for Smithy `@waitable` operations.\n"
"\n"
" The codegen-emitted `wait_until_<name>` functions are thin\n"
" wrappers over `wait`. Each wrapper:\n"
"\n"
" 1. Invokes the underlying typed operation in a `step` closure.\n"
" 2. Matches the result against the waiter's acceptors (a list\n"
" of `state` + `matcher` rules lifted from the Smithy trait).\n"
" Returns `Settled` when an acceptor with `state: success`\n"
" matches, `FailedNow(err)` when `state: failure` matches,\n"
" `Continue` otherwise.\n"
" 3. Calls `wait(step, max_attempts, min_delay, max_delay)` to\n"
" drive the loop with exponential backoff between attempts.\n"
"\n"
" Backoff doubles the previous delay (starting at `min_delay_ms`)\n"
" up to `max_delay_ms` — Smithy's standard waiter cadence. The\n"
" helper sleeps between attempts via `process.sleep`; tests pass\n"
" zero delays so they don't block.\n"
"\n"
" `step` carries the 1-indexed attempt number. The codegen\n"
" generally ignores it but specialised callers can use it for\n"
" dynamic adjustments (e.g. emit a log line per attempt).\n"
).
-type step(OVJ) :: settled | continue | {failed_now, OVJ}.
-type waiter_error(OVK) :: {failed, OVK} | {max_attempts_exceeded, integer()}.
-file("src/aws/waiter.gleam", 103).
?DOC(
" Smithy's waiter backoff: double the previous delay, capped at\n"
" `max_delay_ms`. Starts at `min_delay_ms` (the first sleep) and\n"
" climbs from there.\n"
).
-spec next_delay(integer(), integer()) -> integer().
next_delay(Current_ms, Max_delay_ms) ->
Doubled = Current_ms * 2,
case Doubled > Max_delay_ms of
true ->
Max_delay_ms;
false ->
Doubled
end.
-file("src/aws/waiter.gleam", 111).
-spec sleep(integer()) -> nil.
sleep(Ms) ->
case Ms =< 0 of
true ->
nil;
false ->
gleam_erlang_ffi:sleep(Ms)
end.
-file("src/aws/waiter.gleam", 70).
-spec loop(
fun((integer()) -> step(OVQ)),
integer(),
integer(),
integer(),
integer()
) -> {ok, nil} | {error, waiter_error(OVQ)}.
loop(Step, Attempt, Max_attempts, Current_delay_ms, Max_delay_ms) ->
case Step(Attempt) of
settled ->
{ok, nil};
{failed_now, E} ->
{error, {failed, E}};
continue ->
case Attempt >= Max_attempts of
true ->
{error, {max_attempts_exceeded, Max_attempts}};
false ->
sleep(Current_delay_ms),
loop(
Step,
Attempt + 1,
Max_attempts,
next_delay(Current_delay_ms, Max_delay_ms),
Max_delay_ms
)
end
end.
-file("src/aws/waiter.gleam", 58).
?DOC(
" Drive a waiter step closure to completion, sleeping between\n"
" attempts with exponential backoff capped at `max_delay_ms`.\n"
" Returns `Ok(Nil)` on settle, `Error(Failed(_))` on a\n"
" `state: failure` acceptor match, `Error(MaxAttemptsExceeded(_))`\n"
" when the attempt budget is exhausted. `max_attempts == 0` is a\n"
" defensive guard that returns immediately without invoking\n"
" `step`.\n"
).
-spec wait(fun((integer()) -> step(OVL)), integer(), integer(), integer()) -> {ok,
nil} |
{error, waiter_error(OVL)}.
wait(Step, Max_attempts, Min_delay_ms, Max_delay_ms) ->
case Max_attempts > 0 of
false ->
{error, {max_attempts_exceeded, 0}};
true ->
loop(Step, 1, Max_attempts, Min_delay_ms, Max_delay_ms)
end.