lib/authorizir/error_helpers.ex

defmodule Authorizir.ErrorHelpers do
  @doc """
  A helper that transforms changeset errors into a map of messages.

  assert {:error, changeset} = Accounts.create_user(%{password: "short"})
  assert "password is too short" in errors_on(changeset).password
  assert %{password: ["password is too short"]} = errors_on(changeset)

  """
  @spec errors_on(Ecto.Changeset.t()) :: %{optional(atom) => list(String.t())}
  def errors_on(changeset) do
    Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
      Regex.replace(~r"%{(\w+)}", message, fn _full_match, key ->
        opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
      end)
    end)
  end

  @doc """
  A helper that gets errors for a specific changeset field

  Unlike calling `errors_on(changeset).my_field`, this does not raise a
  `KeyError` if the field has no errors; it instead returns an empty list.
  """
  @spec errors_on(Ecto.Changeset.t(), atom()) :: list(String.t())
  def errors_on(changeset, field) do
    Map.get(errors_on(changeset), field, [])
  end
end