Packages
estructura
1.2.3
1.14.0
1.13.0
1.12.0
1.11.0
1.10.2
1.10.1
1.10.0
1.9.0
1.8.3
1.8.2
1.8.1
1.8.0
1.7.2
1.7.1
1.7.0
1.6.1
1.6.0
1.5.0
1.4.2
1.4.1
1.4.0
1.3.3
1.3.2
1.3.1
1.3.0
1.2.12
1.2.11
1.2.10
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
1.0.0
0.6.0
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
Extensions for Elixir structures.
Current section
Files
Jump to
Current section
Files
lib/estructura/transformer.ex
defprotocol Estructura.Transformer do
@doc """
The function returning the transformed input to be used with `Inspect` protocol.
This protocol is explicitly handful when deeply nested structs are to be logged/inspected.
```elixir
iex|%_{}|1 ▶ %Estructura.User{}
%Estructura.User{
address: %Estructura.User.Address{
city: nil,
street: %Estructura.User.Address.Street{house: nil, name: []}
},
data: %Estructura.User.Data{age: nil},
name: nil
}
iex|%_{}|2 ▶ Estructura.Transformer.transform %Estructura.User{}, type: false, except: ~w|address.street data.age|
[address: [city: nil], data: [], name: nil]
iex|%_{}|3 ▶ Estructura.Transformer.transform %Estructura.User{}, type: false, only: ~w|address.street data.age|
[address: [street: [house: nil, name: []]], data: [age: nil]]
iex|%_{}|4 ▶ Estructura.Transformer.transform %Estructura.User{}, only: ~w|address|
[
*: Estructura.User,
address: [
*: Estructura.User.Address,
city: nil,
street: [*: Estructura.User.Address.Street, house: nil, name: []]
]
]
```
To enable it for your struct, use `@derive Estructura.Transformer` or
`@derive {Estructura.Transformer, options}`. `Estructura` implementations derive it be default.
"""
@spec transform(t(), keyword()) :: term()
def transform(input, options \\ [])
end
defimpl Estructura.Transformer, for: Any do
defmacro __deriving__(module, _struct, options) do
quote do
defimpl Estructura.Transformer, for: unquote(module) do
def transform(input, options) do
options = Keyword.merge(unquote(options), options)
type = Keyword.get(options, :type, true)
{onlies, nested_onlies, grouped_nested_onlies} =
options |> Keyword.get(:only, []) |> split_nesteds()
{excepts, nested_excepts, grouped_nested_excepts} =
options |> Keyword.get(:except, []) |> split_nesteds()
data =
input
|> then(fn struct ->
case onlies ++ Map.keys(grouped_nested_onlies) do
[] -> Map.from_struct(struct)
onlies -> Map.take(struct, onlies)
end
end)
|> Map.drop(excepts)
|> Enum.to_list()
|> Enum.sort()
|> Enum.map(fn {k, v} ->
options =
options
|> Keyword.merge(only: nested_onlies ++ Map.get(grouped_nested_onlies, k, []))
|> Keyword.merge(except: nested_excepts ++ Map.get(grouped_nested_excepts, k, []))
{k, Estructura.Transformer.transform(v, options)}
end)
case type do
true ->
[{:*, unquote(module)} | data]
name when is_atom(name) and name not in [false, nil] ->
[{name, unquote(module)} | data]
false ->
data
end
end
defp split_nesteds(nesteds) do
{to_apply, to_propagate} =
Enum.reduce(nesteds, {[], []}, fn
elem, {to_apply, to_propagate} when is_atom(elem) ->
{[elem | to_apply], [elem | to_propagate]}
elem, {to_apply, to_propagate} when is_binary(elem) ->
elem
|> String.split(".", parts: 2)
|> case do
[last] -> {[String.to_atom(last) | to_apply], to_propagate}
[key, nested] -> {to_apply, [{String.to_atom(key), nested} | to_propagate]}
end
end)
grouped =
to_propagate
|> Enum.reject(&is_atom/1)
|> Enum.group_by(&elem(&1, 0), &elem(&1, 1))
{to_apply, Enum.filter(to_propagate, &is_atom/1), grouped}
end
end
end
end
def transform(input, _options), do: input
end
defimpl Estructura.Transformer, for: Atom do
def transform(atom, _options) do
atom
end
end
defimpl Estructura.Transformer, for: Integer do
def transform(integer, _options) do
integer
end
end
defimpl Estructura.Transformer, for: Float do
def transform(float, _options) do
float
end
end
defimpl Estructura.Transformer, for: [List, Map] do
def transform(enum, options) do
only = Keyword.get(options, :only, [])
except = Keyword.get(options, :except, [])
for {k, v} <- enum,
only == [] or k in only,
k not in except,
do: {k, Estructura.Transformer.transform(v, options)}
end
end
defimpl Estructura.Transformer, for: BitString do
def transform(bitstring, _options) do
bitstring
end
end
defimpl Estructura.Transformer, for: [Date, Time, NaiveDateTime, DateTime] do
def transform(value, _options) do
@for.to_iso8601(value)
end
end
# defimpl Estructura.Transformer, for: Decimal do
# def transform(input, _options) do
# @for.to_string(value)
# end
# end