Current section

Files

Jump to
triage lib triage inspect.ex
Raw

lib/triage/inspect.ex

defmodule Triage.Inspect do
@moduledoc false
# Logic to do smart inspecting of values. Part of that is outputting only the most useful
# attributes for debugging (IDs, names, etc...)
defmodule Wrapper do
@moduledoc false
# Struct to simply hold some term(). To be able to render an inspect string
# by controlling the algebra (see `defimpl Inspect, for: Triage.Inspect.Wrapper do` # below)
# If there's an official way in the Elixir API to take algebra and turn it into a string
# then that would be better
defstruct [:value]
end
def inspect(value, opts \\ []) do
Kernel.inspect(wrap_value(value), opts)
end
defp wrap_value(list) when is_list(list) do
Enum.map(list, &wrap_value/1)
end
defp wrap_value(%mod{} = struct) when is_struct(struct) do
struct
|> Map.from_struct()
|> wrap_value()
|> then(&struct(mod, &1))
|> then(&%Wrapper{value: &1})
end
defp wrap_value(map) when is_map(map) do
Map.new(map, fn {key, value} ->
{key, wrap_value(value)}
end)
end
defp wrap_value(value), do: value
end
defimpl Inspect, for: Triage.Inspect.Wrapper do
def inspect(%{value: value}, opts) do
inspect_value(value, opts)
end
defp inspect_value(%mod{} = struct, opts) do
map = Map.from_struct(struct)
fields =
mod.__info__(:struct)
|> Enum.filter(&include_key_value?(mod, &1.field, map[&1.field]))
|> order_fields()
try do
# The `inspect/4` function was changed to `inspect_as_struct/4` in Elixir 1.19
Inspect.Any.inspect_as_struct(map, Macro.inspect_atom(:literal, mod), fields, opts)
rescue
UndefinedFunctionError ->
Inspect.Any.inspect(map, Macro.inspect_atom(:literal, mod), fields, opts)
end
end
defp inspect_value(value, opts), do: Inspect.inspect(value, opts)
defp order_fields(fields) do
field_index_func = fn
:id -> -3
"id" -> -3
:name -> -2
"name" -> -2
:__struct__ -> -1
"__struct__" -> -1
key -> to_string(key)
end
Enum.sort_by(fields, fn %{field: field} -> field_index_func.(field) end)
end
defp include_key_value?(Ecto.Changeset, key, _) do
key in ~w[action changes data errors params valid?]a
end
defp include_key_value?(_, key, _) when key in [:id, "id", :name, "name", :status, "status"] do
true
end
defp include_key_value?(_, key, value) do
Regex.match?(~r/[a-z](_id|Id|ID)$/, to_string(key)) or
(is_map(value) and map_size(value) > 0) or
is_list(value) or is_struct(value)
end
end