Packages
igniter
0.3.39
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/keyword.ex
defmodule Igniter.Code.Keyword do
@moduledoc """
Utilities for working with keyword.
"""
require Igniter.Code.Common
alias Igniter.Code.Common
alias Sourceror.Zipper
@doc "Returns true if the node is a nested keyword list containing a value at the given path."
@spec keyword_has_path?(Zipper.t(), [atom()]) :: boolean()
def keyword_has_path?(_zipper, []), do: true
def keyword_has_path?(zipper, [key | rest]) do
if Common.node_matches_pattern?(zipper, value when is_list(value)) do
case Igniter.Code.List.move_to_list_item(zipper, 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) do
{:ok, zipper} ->
case Igniter.Code.Tuple.tuple_elem(zipper, 1) do
{:ok, second_elem} ->
keyword_has_path?(second_elem, rest)
_ ->
false
end
_ ->
false
end
else
false
end
end
@doc "Returns true if the node is a nested keyword list containing a value at the given path."
@spec get_key(Zipper.t(), atom()) :: term()
def get_key(zipper, key) do
if Igniter.Code.List.list?(zipper) do
case Igniter.Code.List.move_to_list_item(zipper, 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) do
{:ok, zipper} ->
case Igniter.Code.Tuple.tuple_elem(zipper, 1) do
{:ok, second_elem} ->
{:ok, second_elem}
_ ->
:error
end
_ ->
:error
end
else
:error
end
end
@doc "Puts a value at a path into a keyword, calling `updater` on the zipper at the value if the key is already present"
@spec put_in_keyword(
Zipper.t(),
list(atom()),
term(),
(Zipper.t() -> {:ok, Zipper.t()} | :error) | nil
) ::
{:ok, Zipper.t()} | :error
def put_in_keyword(zipper, path, value, updater \\ nil) do
updater = updater || fn zipper -> {:ok, Common.replace_code(zipper, value)} end
Igniter.Code.Common.within(zipper, fn zipper ->
do_put_in_keyword(zipper, path, value, updater)
end)
end
defp do_put_in_keyword(zipper, [key], value, updater) do
set_keyword_key(zipper, key, value, updater)
end
defp do_put_in_keyword(zipper, [key | rest], value, updater) do
zipper = Common.maybe_move_to_single_child_block(zipper)
if Igniter.Code.List.list?(zipper) do
case Igniter.Code.List.move_to_list_item(zipper, 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) do
:error ->
value =
keywordify(rest, value)
value =
value
|> Sourceror.to_string()
|> Sourceror.parse_string!()
value = Common.use_aliases(value, zipper)
to_append =
case zipper.node do
[{{:__block__, meta, _}, _} | _] ->
if meta[:format] do
{{:__block__, [format: meta[:format]], [key]}, {:__block__, [], [value]}}
else
{{:__block__, [], [key]}, {:__block__, [], [value]}}
end
[] ->
{{:__block__, [format: :keyword], [key]}, {:__block__, [], [value]}}
_current_node ->
{{:__block__, [], [key]}, {:__block__, [], [value]}}
end
{:ok, Zipper.append_child(zipper, to_append)}
{:ok, zipper} ->
zipper
|> Igniter.Code.Tuple.tuple_elem(1)
|> case do
{:ok, zipper} ->
do_put_in_keyword(zipper, rest, value, updater)
:error ->
:error
end
end
end
end
@spec set_keyword_key(
Zipper.t(),
atom(),
term(),
(Zipper.t() -> {:ok, Zipper.t()} | :error)
) ::
{:ok, Zipper.t()} | :error
def set_keyword_key(zipper, key, value, updater) do
zipper = Common.maybe_move_to_single_child_block(zipper)
if Igniter.Code.List.list?(zipper) do
zipper = Common.maybe_move_to_single_child_block(zipper)
case Igniter.Code.List.move_to_list_item(zipper, 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) do
:error ->
to_append =
case zipper.node do
[{{:__block__, meta, _}, _} | _] ->
value =
value
|> Sourceror.to_string()
|> Sourceror.parse_string!()
value = Common.use_aliases(value, zipper)
if meta[:format] do
{{:__block__, [format: meta[:format]], [key]}, value}
else
{{:__block__, [], [key]}, value}
end
[] ->
{{:__block__, [format: :keyword], [key]}, {:__block__, [], [value]}}
_ ->
{key, value}
end
{:ok, Zipper.append_child(zipper, to_append)}
{:ok, zipper} ->
zipper
|> Igniter.Code.Tuple.tuple_elem(1)
|> case do
{:ok, zipper} ->
updater.(zipper)
:error ->
:error
end
end
else
:error
end
end
@doc "Removes a key from a keyword list if present. Returns `:error` only if the node is not a list"
@spec remove_keyword_key(Zipper.t(), atom()) :: {:ok, Zipper.t()} | :error
def remove_keyword_key(zipper, key) do
Common.within(zipper, fn zipper ->
if Igniter.Code.List.list?(zipper) do
case Igniter.Code.List.move_to_list_item(zipper, 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) do
:error ->
{:ok, zipper}
{:ok, zipper} ->
{:ok, zipper |> Zipper.remove()}
end
else
:error
end
end)
end
@doc "Puts into nested keyword lists represented by `path`"
@spec keywordify(path :: [atom()], value :: any()) :: any()
def keywordify([], value) when is_integer(value) or is_float(value) do
{:__block__, [token: to_string(value)], [value]}
end
def keywordify([], value) do
{:__block__, [], [value]}
end
def keywordify([key | rest], value) do
[{{:__block__, [format: :keyword], [key]}, {:__block__, [], [keywordify(rest, value)]}}]
end
end