lib/csrf_plus/error_mapper.ex

defmodule CsrfPlus.ErrorMapper do
  @moduledoc """
  Defines the behaviour for an ErrorModule while also defining a default map function.
  """

  @callback map(exception :: Exception.t()) :: {status_code :: atom() | integer(), error :: map()}

  @behaviour __MODULE__

  @doc "Maps a CsrfPlus exception into a tuple with the status code and the error map."
  @impl __MODULE__
  def map(exception) do
    if CsrfPlus.Exception.csrf_plus_exception?(exception) do
      error =
        %{
          is_csrf_error: true,
          reason: module_to_string(exception.__struct__),
          message: exception.message
        }

      {:unauthorized, error}
    else
      raise exception
    end
  end

  @doc "Converts a module to string, but without the Elixir prefix."
  def module_to_string(module) when is_atom(module) do
    module
    |> to_string()
    |> String.replace_prefix("Elixir.", "")
  end

  def module_from_string(nil) do
    nil
  end

  @doc "Converts back a string representation of a module to an actual module name"
  def module_from_string(string) when is_binary(string) do
    "Elixir."
    |> Kernel.<>(string)
    |> String.to_atom()
  end
end