Skip to content

Commit

Permalink
improvement: better formatting of bread crumbs
Browse files Browse the repository at this point in the history
fix: don't miss adding bread crumbs in specific cases
  • Loading branch information
zachdaniel committed Oct 30, 2024
1 parent 276cf70 commit 582cc9f
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 18 deletions.
8 changes: 5 additions & 3 deletions lib/splode.ex
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,13 @@ defmodule Splode do
@impl true
def to_class(value, opts \\ [])

def to_class(%struct{errors: [error]} = class, _opts)
def to_class(%struct{errors: [error]} = class, opts)
when struct in @class_modules do
if error.class == :special do
error
else
class
|> accumulate_bread_crumbs(opts[:bread_crumbs] |> IO.inspect())
end
end

Expand Down Expand Up @@ -237,6 +238,7 @@ defmodule Splode do
if Enum.count_until(errors, 2) == 1 &&
(Enum.at(errors, 0).class == :special || Enum.at(errors, 0).__struct__.error_class?()) do
List.first(errors)
|> accumulate_bread_crumbs(opts[:bread_crumbs])
else
errors
|> flatten_errors()
Expand Down Expand Up @@ -408,8 +410,8 @@ defmodule Splode do
|> Enum.reduce(error, &accumulate_bread_crumbs(&2, &1))
end

defp accumulate_bread_crumbs(%{errors: [_ | _] = errors} = error, bread_crumbs)
when is_binary(bread_crumbs) do
defp accumulate_bread_crumbs(%module{errors: errors} = error, bread_crumbs)
when is_binary(bread_crumbs) and module in @class_modules do
updated_errors = accumulate_bread_crumbs(errors, bread_crumbs)

add_bread_crumbs(%{error | errors: updated_errors}, bread_crumbs)
Expand Down
3 changes: 2 additions & 1 deletion lib/splode/error.ex
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ defmodule Splode.Error do
end

defmacro __before_compile__(env) do
if Module.defines?(env.module, {:message, 1}, :def) do
if Module.defines?(env.module, {:message, 1}, :def) &&
!Module.get_attribute(env.module, :error_class) do
quote generated: true do
defoverridable message: 1

Expand Down
73 changes: 59 additions & 14 deletions lib/splode/error_class.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ defmodule Splode.ErrorClass do
@doc "Creates a long form composite error message for a list of errors"
def error_messages(errors, opts \\ []) do
custom_message = opts[:custom_message]
errors = List.wrap(errors)

{bread_crumbs, errors} =
extract_shared_bread_crumbs(errors)

generic_message =
errors
|> List.wrap()
|> Enum.group_by(& &1.class)
|> Enum.map_join("\n\n", fn {class, class_errors} ->
header = String.capitalize(to_string(class)) <> " Error\n\n"
Expand All @@ -57,16 +60,9 @@ defmodule Splode.ErrorClass do
" " <> Exception.format_stacktrace_entry(stack_item)
end)

%{bread_crumbs: bread_crumbs} = class_error when is_list(bread_crumbs) ->
if is_exception(class_error) do
bread_crumb(class_error.bread_crumbs) <>
"* #{Exception.message(class_error)}\n" <>
path(class_error)
else
end

other ->
Exception.format(:error, other)
class_error ->
"* #{Exception.message(class_error)}\n" <>
path(class_error)
end)
end)

Expand All @@ -76,12 +72,34 @@ defmodule Splode.ErrorClass do
|> List.wrap()
|> Enum.map_join("\n", &"* #{&1}")

"\n\n" <> custom <> generic_message
"\n#{bread_crumb(bread_crumbs)}\n" <> custom <> generic_message
else
"\n#{bread_crumb(bread_crumbs)}" <>
generic_message
end
end

defp extract_shared_bread_crumbs(errors, shared \\ [])

defp extract_shared_bread_crumbs(
[%{bread_crumbs: [first | _rest_crumbs]} | rest] = errors,
shared
) do
if Enum.any?(rest, fn other_error ->
!is_map(other_error) || !Map.has_key?(other_error, :bread_crumbs) ||
Enum.at(other_error.bread_crumbs, 0) != first
end) do
{Enum.reverse(shared), errors}
else
generic_message
extract_shared_bread_crumbs(
Enum.map(errors, &%{&1 | bread_crumbs: Enum.drop(&1.bread_crumbs, 1)}),
[first | shared]
)
end
end

defp extract_shared_bread_crumbs(errors, bread_crumbs), do: {Enum.reverse(bread_crumbs), errors}

defp path(%{path: path}) when path not in [[], nil] do
" at " <> to_path(path) <> "\n"
end
Expand Down Expand Up @@ -112,7 +130,34 @@ defmodule Splode.ErrorClass do
""

bread_crumbs ->
"Bread Crumbs: " <> Enum.join(bread_crumbs, " > ") <> "\n"
"Bread Crumbs:\n" <> join_bread_crumbs(bread_crumbs) <> "\n"
end
end

defp join_bread_crumbs(bread_crumbs) do
Enum.reduce(bread_crumbs, {"", []}, fn bread_crumb, {line, lines} ->
case String.length(line) + String.length(bread_crumb) do
length when length < 80 ->
{line <> "> " <> bread_crumb, lines}

_ ->
{"> " <> bread_crumb, [" " <> line | lines]}
end
end)
|> then(fn {line, lines} ->
lines =
case line do
"" ->
lines

line ->
[" " <> line | lines]
end

lines
|> Enum.reject(&(&1 == ""))
|> Enum.join("\n")
|> Kernel.<>("\n")
end)
end
end

0 comments on commit 582cc9f

Please sign in to comment.