defmodule VirtualFieldsFiller do @callback fill_virtual_fields(struct) :: struct defguard is_struct_or_list_of_structs(value) when is_struct(value) or (is_list(value) and length(value) > 0 and is_struct(hd(value))) def fill_virtual_fields([]), do: [] def fill_virtual_fields([head | tail]) do [ fill_virtual_fields(head) | fill_virtual_fields(tail) ] end def fill_virtual_fields(%{__struct__: _struct_name} = entity) do entity = if function_exported?(entity.__struct__, :fill_virtual_fields, 1) do entity.__struct__.fill_virtual_fields(entity) else entity end Map.keys(entity) |> Enum.reduce(entity, fn field_name, entity -> case Map.fetch!(entity, field_name) do nested when is_struct_or_list_of_structs(nested) -> struct(entity, [ {field_name, fill_virtual_fields(nested)} ]) _ -> entity end end) end end