lib/async_action/base.ex

defmodule Rephex.AsyncAction.Base do
  alias Phoenix.LiveView.Socket

  # Path to target AsyncResult
  @type result_path :: [term()]
  @type state :: map()
  @type payload :: map()
  @type progress :: any()
  @type success_result :: any()
  @type exit_reason :: any()
  @type failed_value :: any()

  @doc """
  Get initial progress. This value will be set synchronously before the async action starts.
  """
  @callback initial_progress(result_path(), payload()) :: progress()

  @doc """
  Before the async action starts, this callback will be called.
  """
  @callback before_start(Socket.t(), result_path(), payload()) :: Socket.t()

  @doc """
  After the async action is resolved, this callback will be called.
  """
  @callback after_resolve(
              Socket.t(),
              result_path(),
              {:ok, success_result()} | {:exit, exit_reason()}
            ) :: Socket.t()

  @doc """
  Generate failed value. This value will be set to AsyncResult via `AsyncResult.failed/2`.
  """
  @callback generate_failed_value(result_path(), exit_reason()) :: failed_value()

  @doc """
  Start async action.
  """
  @callback start_async(
              state(),
              result_path(),
              payload(),
              (progress() -> nil)
            ) :: success_result()

  @doc """
  This callback will be implemented by __using__.
  """
  @callback options() :: %{optional(:throttle) => non_neg_integer()}

  @optional_callbacks initial_progress: 2,
                      before_start: 3,
                      after_resolve: 3,
                      generate_failed_value: 2
end