defmodule Moar.Retry do
# @related [test](/test/retry_test.exs)
# TODO:
# - implement: retry_until(DateTime.t(), fun()) :: {:ok, any()} | {:error, :timeout}
# - implement: retry_for(pos_integer(), fun()) :: {:ok, any()} | {:error, :timeout}
#
# see: https://github.com/RatioPBC/euclid/blob/main/test/support/helpers/retry.ex
@spec rescue_for!(pos_integer(), (() -> any())) :: any() | no_return
def rescue_for!(timeout_ms \\ 5000, fun) when is_function(fun) and is_number(timeout_ms) do
DateTime.utc_now()
|> DateTime.add(timeout_ms, :millisecond)
|> rescue_until!(fun)
end
@spec rescue_until!(DateTime.t(), (() -> any())) :: any() | no_return
def rescue_until!(%DateTime{} = time, fun) when is_function(fun) do
fun.()
rescue
e ->
if DateTime.compare(time, DateTime.utc_now()) == :gt do
:timer.sleep(100)
rescue_until!(time, fun)
else
reraise e, __STACKTRACE__
end
end
end