lib/routex/extension.ex

defmodule Routex.Extension do
  @moduledoc """
  Specification for composable Routex extensions.

  All callbacks are *optional*

  See also: [Routex Extensions](EXTENSION_DEVELOPMENT.md)
  """

  alias Routex.Types, as: T

  @doc """
  The `configure/2` callback is called in the first stage with the options
  provided to `Routex` and the name of the Routex backend. It is expected to
  return a new list of options.
  """
  @callback configure(T.opts(), T.backend()) :: T.opts()

  @doc """
  The `transform/3` callback is called in the second stage with a list of
  routes belonging to a Routex backend, the name of the configuration model
  and the current environment. It is expected to return a list of
  Phoenix.Router.Route structs with flattened `Routex.Attrs`.
  """
  @callback transform(T.routes(), T.backend(), T.env()) :: T.routes()

  @doc """
  The `post_transform/1` callback is called in the third stage with a list of
  routes belonging to a Routex backend. It is expected to return a list of
  Phoenix.Router.Route structs almost identical to the input, only adding
  `Routex.Attrs` -for own usage- is allowed.
  """
  @callback post_transform(T.routes(), T.backend(), T.env()) :: T.routes()

  @doc """
  The `create_helpers/3` callback is called in the last stage with a list of
  routes belonging to a Routex backend, the name of the Routex backend and the
  current environment. It is expected to return Elixir AST.

  This can be used to create dedicated backend. As multiple backends may include
  the extension, no catchall fallback is allowed. (see:
  `create_shared_helpers/3`).

  ```elixir
  def my_fun(%{arg: RoutexBackend.Default}), do: :foo
  ```

  The AST is included in `MyAppWeb.Router.RoutexHelpers`.
  """
  @callback create_helpers(T.routes(), T.backend(), T.env()) :: T.ast()

  @doc """
  The `create_shared_helpers/3` callback is called in the last stage. It differs
  from `create_helpers/3` as it is called only once per extension with routes
  combined of all backends using the extension.

  This can be used to create cross-backend (combined) helpers or dedicated
  backend helpers combined with an additional catch all function head.

  ```elixir
  def my_fun(%{arg: RoutexBackend.Default}), do: :foo
  def my_fun(%{arg: RoutexBackend.Other}), do: :bar
  def my_fun(_), do: :biz
  ```

  or

  ```elixir
  def my_fun(socket) do
    ...some shared code...
    case socket_to_backend(socket) do
      %{arg: RoutexBackend.Default} ->  :foo
      %{arg: RoutexBackend.Other} ->  :bar
       _ ->  :biz
    end
  end
  ```

  The AST is included in `MyAppWeb.Router.RoutexHelpers`.
  """
  @callback create_shared_helpers(T.routes(), [T.backend(), ...], T.env()) :: T.ast()

  @optional_callbacks [
    configure: 2,
    transform: 3,
    post_transform: 3,
    create_helpers: 3,
    create_shared_helpers: 3
  ]
end