defmodule Mentor.Ecto.Error do @moduledoc false alias Ecto.Changeset def format_errors(%Changeset{} = changeset) do errors = Ecto.Changeset.traverse_errors(changeset, fn _changeset, _field, {msg, opts} -> msg = Regex.replace(~r"%{(\w+)}", msg, fn _, key -> opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string() end) "#{msg}" end) format_errors(errors) end def format_errors(errors) do format_errors(errors, []) end defp format_errors(%{} = errors, path) do errors |> Enum.map_join("\n", fn {key, value} -> format_errors(value, path ++ [key]) end) end defp format_errors([%{} = map | tail], path) do Enum.with_index([map | tail]) |> Enum.map_join("\n", fn {element, index} -> format_errors(element, path ++ [index]) end) end defp format_errors([head | tail], path) do formatted_head = format_error(head, path) formatted_tail = format_errors(tail, path) [formatted_head, formatted_tail] |> Enum.filter(&(&1 != "")) |> Enum.join("\n") end defp format_errors([], _path), do: "" defp format_errors(value, path) when is_binary(value) do format_error(value, path) end defp format_error(error, path) do formatted_path = Enum.map_join(path, ".", fn element when is_integer(element) -> "[#{element}]" element -> to_string(element) end) "#{formatted_path} - #{error}" end end