Packages

A normalization/denormalization that works well with Elixir datastructures.

Current section

Files

Jump to
hugs lib hugs beam_info.ex
Raw

lib/hugs/beam_info.ex

defmodule Hugs.BeamInfo do
@builtin_types Hugs.TypeDef.builtin_types()
def struct_type(module) when is_atom(module) do
case :code.which(module) do
[] ->
{:error, "cannot retrieve beam file for #{module}"}
file ->
case :beam_lib.chunks(file, [:debug_info]) do
{:error, :beam_lib, reason} -> {:error, {:beam_lib, reason}}
{:ok, {^module, [{:debug_info, info}]}} -> struct_type(module, info)
end
end
end
defp struct_type(module, {:debug_info_v1, _dbgmod, {:elixir_v1, _map, specs}}) do
specs
|> Enum.find_value(&find_struct_type(&1, module))
end
defp find_struct_type({:attribute, _, :type, {_type_name, {:type, _, :map, fields}, _}}, module) do
case Enum.find(fields, &has_struct_name_field?(&1, module)) do
nil -> false
{:type, _, :map_field_exact, _} -> collect_fields_types(fields)
end
end
defp find_struct_type(_, _module) do
false
end
defp has_struct_name_field?(
{:type, _, :map_field_exact, [{:atom, _, :__struct__}, {:atom, _, module}]},
module
) do
true
end
defp has_struct_name_field?(_, _) do
false
end
defp collect_fields_types(fields) do
Enum.map(fields, &collect_field_types/1)
end
defp collect_field_types(
{:type, _, :map_field_exact, [{:atom, _, :__struct__}, {:atom, _, module}]}
) do
{:struct_name, module}
end
defp collect_field_types(
{:type, _, :map_field_exact, [{:atom, _, field_name}, {:type, _, :union, unions}]}
) do
{:field, {field_name, {:t_union, Enum.map(unions, &cast_type/1)}}}
end
defp collect_field_types({:type, _, :map_field_exact, [{:atom, _, field_name}, type]}) do
{:field, {field_name, [cast_type(type)]}}
end
defp cast_type({:type, _, builtin, []}) when builtin in @builtin_types,
do: {:t_builtin, builtin}
defp cast_type({:remote_type, _, [{:atom, _, mod}, {:atom, 0, type}, _]}),
do: {:t_remote, {mod, type}}
defp cast_type({:user_type, _, type, _}), do: {:t_user, type}
end