Packages

plymio_codi: Generating Quoted Forms for Common Code Patterns

Current section

Files

Jump to
plymio_codi lib codi pattern proxy.ex
Raw

lib/codi/pattern/proxy.ex

defmodule Plymio.Codi.Pattern.Proxy do
@moduledoc false
alias Plymio.Codi, as: CODI
use Plymio.Fontais.Attribute
use Plymio.Codi.Attribute
import Plymio.Fontais.Guard,
only: [
is_value_unset_or_nil: 1
]
import Plymio.Codi.Error,
only: [
new_error_result: 1
]
import Plymio.Fontais.Option,
only: [
opts_create_aliases_dict: 1
]
import Plymio.Funcio.Enum.Map.Collate,
only: [
map_collate0_enum: 2
]
import Plymio.Codi.Utility,
only: [
opts_resolve_proxy_names: 1
]
import Plymio.Fontais.Form,
only: [
forms_normalise: 1
]
import Plymio.Fontais.Form,
only: [
forms_edit: 2,
forms_normalise: 1
]
import Plymio.Codi.Utility.GetSet,
only: [
cpo_normalise: 2,
cpo_done_with_form: 2
]
@pattern_proxy_kvs_alias [
@plymio_codi_key_alias_pattern,
@plymio_codi_key_alias_status,
@plymio_codi_key_alias_form,
@plymio_codi_key_alias_proxy_name,
@plymio_codi_key_alias_transform,
@plymio_codi_key_alias_postwalk
]
@pattern_proxy_dict_alias @pattern_proxy_kvs_alias
|> opts_create_aliases_dict
def cpo_pattern_proxy_normalise(cpo, dict \\ nil) do
cpo |> cpo_normalise(dict || @pattern_proxy_dict_alias)
end
def opts_dict_canonical_keys() do
@pattern_proxy_dict_alias
end
def express_pattern(codi, pattern, opts)
def express_pattern(%CODI{@plymio_codi_field_vekil => vekil_dict}, pattern, _cpo)
when pattern == @plymio_codi_pattern_proxy and is_value_unset_or_nil(vekil_dict) do
new_error_result(m: "vekil missing")
end
def express_pattern(%CODI{@plymio_codi_field_vekil => vekil_dict} = state, pattern, cpo)
when pattern == @plymio_codi_pattern_proxy do
with {:ok, cpo} <- cpo |> cpo_pattern_proxy_normalise,
{:ok, proxy_names} <- cpo |> opts_resolve_proxy_names,
{:ok, forms} <- vekil_dict |> resolve_vekil_proxies(proxy_names),
{:ok, forms} <- forms |> forms_edit(cpo),
{:ok, cpo} <- cpo |> cpo_done_with_form(forms) do
{:ok, {cpo, state}}
else
{:error, %{__exception__: true}} = result -> result
end
end
def express_pattern(_codi, pattern, opts) do
new_error_result(m: "proxy pattern #{inspect(pattern)} invalid", v: opts)
end
# recurses
defp resolve_vekil_proxy(vekil_dict, proxy_name, seen_proxies) do
vekil_dict
|> Map.fetch(proxy_name)
|> case do
{:ok, value} ->
value
|> List.wrap()
|> Enum.reduce_while([], fn
new_proxy, forms when is_atom(new_proxy) ->
# seen before i.e. looping?
seen_proxies
|> Map.has_key?(new_proxy)
|> case do
true ->
{:halt, new_error_result(m: "proxy seen before", v: new_proxy)}
_ ->
seen_proxies = seen_proxies |> Map.put(new_proxy, nil)
with {:ok, new_forms} <-
vekil_dict |> resolve_vekil_proxy(new_proxy, seen_proxies) do
{:cont, [new_forms | forms]}
else
{:error, %{__struct__: _}} = result -> {:halt, result}
end
end
# must be a form - will be validated later
form, forms ->
{:cont, [form | forms]}
end)
|> case do
{:error, %{__struct__: _}} = result ->
result
forms ->
{:ok, forms |> Enum.reverse()}
end
_ ->
new_error_result(m: "proxy not found", v: proxy_name)
end
end
defp resolve_vekil_proxies(vekil_dict, proxies) do
proxies
|> List.wrap()
|> map_collate0_enum(fn proxy_name ->
vekil_dict |> resolve_vekil_proxy(proxy_name, %{})
end)
|> case do
{:error, %{__struct__: _}} = result ->
result
{:ok, forms} ->
forms |> forms_normalise
end
end
end