Packages
igniter
0.3.50
0.8.2
0.8.1
0.8.0
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.30
0.6.29
0.6.28
0.6.27
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.52
0.5.51
0.5.50
0.5.49
0.5.48
0.5.47
0.5.46
0.5.45
0.5.44
0.5.43
0.5.42
0.5.41
0.5.40
0.5.39
0.5.38
0.5.37
0.5.36
0.5.35
0.5.34
0.5.33
0.5.32
0.5.31
0.5.30
0.5.29
0.5.28
0.5.27
0.5.26
0.5.25
0.5.24
0.5.23
0.5.22
0.5.21
0.5.20
0.5.19
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.78
0.3.77
0.3.76
0.3.75
0.3.74
0.3.73
0.3.72
0.3.71
0.3.70
0.3.69
0.3.68
0.3.67
0.3.66
0.3.65
0.3.64
0.3.63
0.3.62
0.3.61
0.3.60
0.3.59
0.3.58
0.3.57
0.3.56
0.3.55
0.3.54
0.3.53
0.3.52
0.3.51
0.3.50
0.3.49
0.3.48
0.3.47
0.3.46
0.3.45
0.3.44
0.3.43
0.3.42
0.3.41
0.3.40
0.3.39
0.3.38
0.3.37
0.3.36
0.3.35
0.3.34
0.3.33
0.3.32
0.3.31
0.3.30
0.3.29
0.3.28
0.3.27
0.3.26
0.3.25
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A code generation and project patching framework
Current section
Files
Jump to
Current section
Files
lib/igniter/code/map.ex
defmodule Igniter.Code.Map do
@moduledoc """
Utilities for working with maps.
"""
require Igniter.Code.Common
alias Igniter.Code.Common
alias Sourceror.Zipper
@doc "Puts a value at a path into a map, calling `updater` on the zipper at the value if the key is already present"
@spec put_in_map(
Zipper.t(),
list(term()),
term(),
(Zipper.t() -> {:ok, Zipper.t()} | :error) | nil
) ::
{:ok, Zipper.t()} | :error
def put_in_map(zipper, path, value, updater \\ nil) do
updater = updater || fn zipper -> {:ok, Common.replace_code(zipper, value)} end
do_put_in_map(zipper, path, value, updater)
end
defp do_put_in_map(zipper, [key], value, updater) do
set_map_key(zipper, key, value, updater)
end
defp do_put_in_map(zipper, [key | rest], value, updater) do
cond do
Common.node_matches_pattern?(zipper, {:%{}, _, []}) ->
value = Common.use_aliases(value, zipper)
{:ok,
Zipper.append_child(
zipper,
mappify([key | rest], value)
)}
Common.node_matches_pattern?(zipper, {:%{}, _, _}) ->
zipper
|> Zipper.down()
|> Igniter.Code.List.move_to_list_item(fn item ->
if Igniter.Code.Tuple.tuple?(item) do
case Igniter.Code.Tuple.tuple_elem(item, 0) do
{:ok, first_elem} ->
Common.nodes_equal?(first_elem, key)
:error ->
false
end
end
end)
|> case do
:error ->
value = Common.use_aliases(value, zipper)
format = map_keys_format(zipper)
value = mappify(rest, value)
{:ok,
Zipper.append_child(
zipper,
{{:__block__, [format: format], [key]}, {:__block__, [], [value]}}
)}
{:ok, zipper} ->
zipper
|> Igniter.Code.Tuple.tuple_elem(1)
|> case do
{:ok, zipper} ->
do_put_in_map(zipper, rest, value, updater)
:error ->
:error
end
end
true ->
:error
end
end
@doc "Puts a key into a map, calling `updater` on the zipper at the value if the key is already present"
@spec set_map_key(Zipper.t(), term(), term(), (Zipper.t() -> {:ok, Zipper.t()} | :error)) ::
{:ok, Zipper.t()} | :error
def set_map_key(zipper, key, value, updater) do
cond do
Common.node_matches_pattern?(zipper, {:%{}, _, []}) ->
value = Common.use_aliases(value, zipper)
{:ok,
Zipper.append_child(
zipper,
mappify([key], value)
)}
Common.node_matches_pattern?(zipper, {:%{}, _, _}) ->
zipper
|> Zipper.down()
|> Common.move_right(fn item ->
if Igniter.Code.Tuple.tuple?(item) do
case Igniter.Code.Tuple.tuple_elem(item, 0) do
{:ok, first_elem} ->
Common.nodes_equal?(first_elem, key)
:error ->
false
end
end
end)
|> case do
:error ->
value = Common.use_aliases(value, zipper)
format = map_keys_format(zipper)
{:ok,
Zipper.append_child(
zipper,
{{:__block__, [format: format], [key]}, {:__block__, [], [value]}}
)}
{:ok, zipper} ->
zipper
|> Igniter.Code.Tuple.tuple_elem(1)
|> case do
{:ok, zipper} ->
updater.(zipper)
:error ->
:error
end
end
true ->
:error
end
end
defp map_keys_format(zipper) do
case zipper.node do
value when is_list(value) ->
Enum.all?(value, fn
{:__block__, meta, _} ->
meta[:format] == :keyword
_ ->
false
end)
|> case do
true ->
:keyword
false ->
:map
end
_ ->
:map
end
end
@doc "Puts a value into nested maps at the given path"
def mappify([], value) do
value
end
def mappify([key | rest], value) do
format =
if is_atom(key) do
:keyword
else
:map
end
{:%{}, [], [{{:__block__, [format: format], [key]}, mappify(rest, value)}]}
end
end