lib/wait_for_it/waitable/waitable.ex

defprotocol WaitForIt.Waitable do
  @moduledoc """
  Protocol used for evaluating waitable expressions against waiting conditions to determine if
  waiting should continue or halt with a final value.
  """

  @type wait_type :: atom()
  @type value :: any()

  @spec wait_type(t()) :: wait_type()
  def wait_type(waitable)

  @doc """
  Evaluates the waitable expression to provide its value, or to continue to wait.

  It should return `{:halt, value}` if the wait is over and the final value of the waitable
  expression has been determined, or `{:cont, value}` if waiting should continue.
  """
  @spec evaluate(t(), Macro.Env.t()) :: {:halt, value()} | {:cont, value()}
  def evaluate(waitable, env)

  @doc """
  Provides the final value of the waitable expression in the event of a timeout.
  """
  @spec handle_timeout(t(), value(), Macro.Env.t()) :: value()
  def handle_timeout(waitable, last_value, env)
end

defprotocol WaitForIt.Waitable.Raise do
  @moduledoc """
  Protocol used to customize exceptions that are raised in the event of a timeout.
  """

  @fallback_to_any true

  @spec raise_timeout_error(
          t(),
          WaitForIt.Waitable.value(),
          timeout_ms :: non_neg_integer(),
          Macro.Env.t()
        ) ::
          no_return()
  def raise_timeout_error(waitable, last_value, timeout_ms, env)
end

defimpl WaitForIt.Waitable.Raise, for: Any do
  def raise_timeout_error(waitable, last_value, timeout_ms, env) do
    raise WaitForIt.TimeoutError,
      waitable: waitable,
      timeout: timeout_ms,
      last_value: last_value,
      env: env
  end
end