Current section
Files
Jump to
Current section
Files
lib/codi/utility/utility.ex
defmodule Plymio.Codi.Utility do
@moduledoc false
use Plymio.Fontais.Attribute
use Plymio.Codi.Attribute
import Plymio.Fontais.Guard,
only: [
is_positive_integer: 1,
is_negative_integer: 1,
is_value_set: 1,
is_value_unset: 1,
is_value_unset_or_nil: 1
]
import Plymio.Codi.Error,
only: [
new_error_result: 1
]
import Plymio.Fontais.Utility,
only: [
validate_key: 1
]
import Plymio.Fontais.Option,
only: [
opzioni_validate: 1,
opts_normalise: 1,
opts_validate: 1,
opts_get: 2,
opts_get: 3,
opts_put: 3,
opts_fetch: 2
]
import Plymio.Fontais.Form,
only: [
form_validate: 1,
forms_normalise: 1
]
import Plymio.Funcio.Enum.Map.Collate,
only: [
map_collate0_enum: 2
]
import Plymio.Fontais.Result,
only: [
normalise1_result: 1
]
import Plymio.Codi.CPO
@type error :: Plymio.Codi.error()
defp validate_vars(vars)
defp validate_vars([]) do
{:ok, []}
end
defp validate_vars(vars) when is_list(vars) do
vars
|> Macro.validate()
|> case do
:ok ->
{:ok, vars}
_ ->
new_error_result(m: "vars invalid", v: vars)
end
end
defp normalise_vars(vars, opts \\ [])
defp normalise_vars([], _) do
{:ok, []}
end
defp normalise_vars(vars, opts) do
vars
|> List.wrap()
|> case do
[] ->
[]
x when is_list(x) ->
x
|> Enum.map(fn
arg when is_atom(arg) ->
arg |> Macro.var(nil)
# +ve => generate e.g. arg1, arg2 etc
arg when is_positive_integer(arg) ->
opts
# prefix for generated var?
|> Keyword.get(@plymio_codi_key_prefix)
|> case do
x when is_nil(x) ->
arg |> Macro.generate_arguments(nil)
x when is_atom(x) ->
Range.new(0, arg - 1)
|> Enum.map(fn v ->
"#{to_string(x)}#{inspect(v)}"
|> String.to_atom()
|> Macro.var(nil)
end)
end
# -ve => generate same prefix var
# (use for type related argument hence any as default prefix)
arg when is_negative_integer(arg) ->
opts
# prefix for generated var?
|> Keyword.get(@plymio_codi_key_prefix, :any)
|> Macro.var(nil)
|> List.duplicate(arg |> abs())
# already a var? will be validated below
arg ->
arg
end)
|> List.flatten()
end
|> validate_vars
end
defp validate_fun_name(name)
defp validate_fun_name(name) when is_atom(name) do
{:ok, name}
end
defp validate_fun_name(name) do
new_error_result(m: "function name invalid", v: name)
end
def validate_fun_module(module)
def validate_fun_module(nil) do
new_error_result(m: "function module invalid", v: nil)
end
def validate_fun_module(module) when is_atom(module) do
{:ok, module}
end
def validate_fun_module(module) do
new_error_result(m: "function module invalid", v: module)
end
def validate_fun_modules(modules)
def validate_fun_modules(modules) when is_list(modules) do
modules
|> map_collate0_enum(&validate_fun_module/1)
end
def validate_fun_modules(modules) do
new_error_result(m: "functions modules invalid", v: modules)
end
defp validate_fun_arity(arity)
defp validate_fun_arity(arity) when is_positive_integer(arity) do
{:ok, arity}
end
defp validate_fun_arity(arity) do
new_error_result(m: "function arity invalid", v: arity)
end
def validate_fun_args(fun_args, ctrl \\ [])
def validate_fun_args(fun_args, ctrl) when is_list(fun_args) do
with {:ok, fun_arity} <- ctrl |> ctrl_get_fun_arity_value do
cond do
is_value_unset(fun_arity) ->
{:ok, fun_args}
is_positive_integer(fun_arity) ->
case fun_arity === length(fun_args) do
true ->
{:ok, fun_args}
_ ->
new_error_result(
m: "function arity constraint #{inspect(fun_arity)} not met",
v: fun_args
)
end
true ->
new_error_result(m: "function arity constraint invalid", v: fun_arity)
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def validate_fun_args(fun_args, _ctrl) do
new_error_result(m: "fun args invalid", v: fun_args)
end
# many are convenience mnemonics
defp normalise_type_arg(result)
defp normalise_type_arg(args) when is_list(args) do
args
|> map_collate0_enum(&normalise_type_arg/1)
end
defp normalise_type_arg(key)
when key in [
:result,
:ok_any_error_error
] do
{:ok, quote(do: {:ok, any} | {:error, error})}
end
defp normalise_type_arg(key)
when key in [
:ok_result,
:ok_any,
:ok_value
] do
{:ok, quote(do: {:ok, any})}
end
defp normalise_type_arg(key)
when key in [
:error_result,
:error_error
] do
{:ok, quote(do: {:error, error})}
end
defp normalise_type_arg(key)
when key in [
:bang_result,
:any_no_return
] do
{:ok, quote(do: any | no_return)}
end
defp normalise_type_arg(:ok_opts_error_error) do
{:ok, quote(do: {:ok, opts} | {:error, error})}
end
defp normalise_type_arg(:opts_no_return) do
{:ok, quote(do: opts | no_return)}
end
defp normalise_type_arg(:ok_atom) do
{:ok, quote(do: {:ok, atom})}
end
defp normalise_type_arg(:ok_binary) do
{:ok, quote(do: {:ok, binary})}
end
defp normalise_type_arg(:ok_keyword) do
{:ok, quote(do: {:ok, keyword})}
end
defp normalise_type_arg(:ok_opts) do
{:ok, quote(do: {:ok, keyword})}
end
defp normalise_type_arg(:ok_list) do
{:ok, quote(do: {:ok, list})}
end
defp normalise_type_arg(:ok_map) do
{:ok, quote(do: {:ok, map})}
end
defp normalise_type_arg(:ok_struct) do
{:ok, quote(do: {:ok, struct})}
end
defp normalise_type_arg(:ok_t) do
{:ok, quote(do: {:ok, t})}
end
defp normalise_type_arg(:ok_tuple) do
{:ok, quote(do: {:ok, tuple})}
end
defp normalise_type_arg(:ok_boolean) do
{:ok, quote(do: {:ok, boolean})}
end
defp normalise_type_arg(:ok_integer) do
{:ok, quote(do: {:ok, integer})}
end
defp normalise_type_arg(:ok_float) do
{:ok, quote(do: {:ok, float})}
end
defp normalise_type_arg(:atom_result) do
{:ok, quote(do: {:ok, atom} | {:error, error})}
end
defp normalise_type_arg(:binary_result) do
{:ok, quote(do: {:ok, binary} | {:error, error})}
end
defp normalise_type_arg(:list_result) do
{:ok, quote(do: {:ok, list} | {:error, error})}
end
defp normalise_type_arg(:keyword_result) do
{:ok, quote(do: {:ok, keyword} | {:error, error})}
end
defp normalise_type_arg(:opts_result) do
{:ok, quote(do: {:ok, keyword} | {:error, error})}
end
defp normalise_type_arg(:map_result) do
{:ok, quote(do: {:ok, map} | {:error, error})}
end
defp normalise_type_arg(:struct_result) do
{:ok, quote(do: {:ok, struct} | {:error, error})}
end
defp normalise_type_arg(:t_result) do
{:ok, quote(do: {:ok, t} | {:error, error})}
end
defp normalise_type_arg(:tuple_result) do
{:ok, quote(do: {:ok, tuple} | {:error, error})}
end
defp normalise_type_arg(:integer_result) do
{:ok, quote(do: {:ok, integer} | {:error, error})}
end
defp normalise_type_arg(:float_result) do
{:ok, quote(do: {:ok, float} | {:error, error})}
end
defp normalise_type_arg(key) when is_atom(key) do
{:ok, Macro.var(key, nil)}
end
defp normalise_type_arg(result) do
result |> normalise_vars
end
defp cpo_resolve(cpo, ctrl) do
with {:ok, cpo} <- cpo |> opts_validate,
{:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, key} <- ctrl |> ctrl_fetch_key,
{:ok, key} <- key |> validate_key,
{:ok, fun} <- ctrl |> ctrl_get_fun_validate_value do
cpo
|> Keyword.has_key?(key)
|> case do
true ->
with {:ok, value} <- cpo |> opts_fetch(key) do
value
|> fun.()
|> normalise1_result
else
{:error, %{__exception__: true}} = result -> result
end
# any default?
_ ->
ctrl
|> ctrl_has_fun_default_value?
|> case do
true ->
with {:ok, default_value} <- ctrl |> ctrl_fetch_fun_default_value do
default_value
|> case do
# don't validate if unset
x when is_value_unset(x) ->
{:ok, x}
x ->
x
|> fun.()
|> normalise1_result
end
else
{:error, %{__exception__: true}} = result -> result
end
_ ->
{:ok, @plymio_fontais_the_unset_value}
end
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_fun_module(cpo, ctrl \\ [])
# can return :ok with nil or unset
def cpo_resolve_fun_module(cpo, ctrl) do
with {:ok, cpo} <- cpo |> opts_validate,
{:ok, fun_module_key} <- ctrl |> ctrl_get_fun_module_key,
{:ok, ctrl} <- ctrl |> ctrl_put_key(fun_module_key),
{:ok, ctrl} <- ctrl |> ctrl_put_fun_validate_value(&validate_fun_module/1),
{:ok, ctrl} <- ctrl |> ctrl_put_fun_default_value(@plymio_fontais_the_unset_value),
{:ok, _fun_module} = result <- cpo |> cpo_resolve(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_fun_name(cpo, ctrl \\ [])
def cpo_resolve_fun_name(cpo, ctrl) do
with {:ok, cpo} <- cpo |> opts_validate,
{:ok, fun_name_key} <- ctrl |> ctrl_get_fun_name_key,
{:ok, fun_name_key} <- fun_name_key |> validate_key,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_validate_value(&validate_fun_name/1),
{:ok, ctrl} <- ctrl |> ctrl_put_key(fun_name_key),
{:ok, _fun_name} = result <- cpo |> cpo_resolve(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_fun_args(cpo, ctrl \\ [])
def cpo_resolve_fun_args(cpo, ctrl) do
with {:ok, cpo_args} <- cpo |> opts_validate,
{:ok, ctrl_args} <- ctrl |> opts_validate,
{:ok, fun_args_key} <- ctrl_args |> ctrl_get_fun_args_key,
{:ok, fun_arity_key} <- ctrl_args |> ctrl_get_fun_arity_key,
{:ok, ctrl_args} <- ctrl |> ctrl_put_fun_validate_value(&resolve_fun_args/1),
{:ok, ctrl_args} <- ctrl_args |> ctrl_put_key(fun_args_key),
{:ok, ctrl_args} <-
ctrl_args |> ctrl_put_fun_default_value(@plymio_fontais_the_unset_value),
{:ok, fun_args} <- cpo_args |> cpo_resolve(ctrl_args) do
fun_args
|> is_value_set
|> case do
true ->
{:ok, fun_args}
_ ->
cpo_args
|> Keyword.get(fun_arity_key, @plymio_fontais_the_unset_value)
|> is_value_set
|> case do
true ->
with {:ok, fun_arity} <- cpo_args |> cpo_resolve_guard_fun_arity(ctrl_args),
{:ok, _} = result <- fun_arity |> resolve_fun_args do
result
else
{:error, %{__exception__: true}} ->
new_error_result(m: "fun arity invalid", v: cpo)
end
_ ->
{:ok, @plymio_fontais_the_unset_value}
end
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_guard_fun_args(cpo, ctrl \\ [])
def cpo_resolve_guard_fun_args(cpo, ctrl) do
with {:ok, fun_args} <- cpo |> cpo_resolve_fun_args(ctrl) do
fun_args
|> is_value_set
|> case do
true ->
fun_args |> validate_fun_args(ctrl)
_ ->
new_error_result(m: "fun args invalid", v: fun_args)
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_fun_arity(cpo, opts \\ [])
def cpo_resolve_fun_arity(cpo, opts) do
with {:ok, cpo_arity} <- cpo |> opts_validate,
{:ok, opts_arity} <- opts |> opts_validate,
{:ok, fun_arity_key} <- opts_arity |> ctrl_get_fun_arity_key,
{:ok, fun_args_key} <- opts_arity |> ctrl_get_fun_args_key,
{:ok, opts_arity} <- opts_arity |> ctrl_put_fun_validate_value(&validate_fun_arity/1),
{:ok, opts_arity} <- opts_arity |> ctrl_put_key(fun_arity_key),
{:ok, opts_arity} <-
opts_arity |> ctrl_put_fun_default_value(@plymio_fontais_the_unset_value),
{:ok, fun_arity} <- cpo_arity |> cpo_resolve(opts_arity) do
fun_arity
|> is_value_set
|> case do
true ->
{:ok, fun_arity}
_ ->
cpo_arity
|> Keyword.get(fun_args_key, @plymio_fontais_the_unset_value)
|> is_value_set
|> case do
true ->
with {:ok, fun_args} <- cpo_arity |> cpo_resolve_guard_fun_args(opts_arity) do
{:ok, fun_args |> length}
else
{:error, %{__exception__: true}} ->
new_error_result(m: "fun arity invalid", v: cpo)
end
_ ->
{:ok, @plymio_fontais_the_unset_value}
end
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_guard_fun_arity(cpo, ctrl \\ [])
def cpo_resolve_guard_fun_arity(cpo, ctrl) do
with {:ok, fun_arity} <- cpo |> cpo_resolve_fun_arity(ctrl) do
fun_arity
|> is_integer
|> case do
true ->
{:ok, fun_arity}
_ ->
new_error_result(m: "fun arity invalid", v: fun_arity)
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_fun_doc(cpo, opts \\ [])
def cpo_resolve_fun_doc(cpo, opts) do
with {:ok, cpo} <- cpo |> opts_validate,
{:ok, fun_doc_key} <- opts |> ctrl_get_fun_doc_key,
{:ok, opts} <- opts |> ctrl_put_key(fun_doc_key),
{:ok, opts} <- opts |> ctrl_put_fun_default_value(@plymio_fontais_the_unset_value),
{:ok, _fun_doc} = result <- cpo |> cpo_resolve(opts) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_guard_fun_field(cpo)
def cpo_resolve_guard_fun_field(cpo) do
cpo
|> cpo_fetch_fun_key
|> case do
{:ok, fun_field} ->
fun_field
|> validate_key
|> case do
{:ok, _fun_field} = result ->
result
_ ->
new_error_result(m: "fun field invalid", v: fun_field)
end
_ ->
new_error_result(m: "fun field missing", v: cpo)
end
end
def cpo_resolve_guard_fun_fields(cpo, ctrl \\ [])
def cpo_resolve_guard_fun_fields(cpo, ctrl) do
with {:ok, cpo} <- cpo |> cpo_normalise,
{:ok, fun_fields} <- cpo |> cpo_fetch_fun_key,
{:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, fun_default} <- ctrl |> ctrl_get_fun_default_value,
{:ok, fun_key_length} <- ctrl |> ctrl_get_fun_key_length,
true <- true do
fun_fields
|> List.wrap()
|> map_collate0_enum(fn
{k, v} when is_atom(k) -> {:ok, {k, v}}
k when is_atom(k) -> {:ok, {k, fun_default}}
x -> new_error_result(m: "fun field invalid", v: x)
end)
|> case do
{:error, %{__struct__: _}} = result ->
result
{:ok, fun_fields} ->
# any constraint on the number of fields?
fun_key_length
|> is_value_set
|> case do
true ->
case fun_key_length === length(fun_fields) do
true ->
{:ok, fun_fields}
_ ->
new_error_result(
m: "function key/field length constraint #{inspect(fun_key_length)} not met",
v: fun_fields
)
end
# no constraint
_ ->
{:ok, fun_fields}
end
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_guard_field_values(cpo, ctrl \\ [])
def cpo_resolve_guard_field_values(cpo, ctrl) do
with {:ok, field_tuples} <- cpo |> cpo_resolve_guard_fun_fields(ctrl),
{:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, fun_build} <- ctrl |> ctrl_get_fun_build_value(&field_build_named_var/1),
true <- true do
field_tuples
|> Enum.with_index()
|> map_collate0_enum(fun_build)
|> case do
{:error, %{__struct__: _}} = result -> result
{:ok, field_values} -> {:ok, {field_values, field_tuples}}
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def field_build_named_var({{field, _value}, index})
when is_atom(field) and is_integer(index) do
index
|> case do
0 ->
{:ok, {field, :field_value |> Macro.var(nil)}}
_ ->
{:ok, {field, "field_value#{to_string(index)}" |> String.to_atom() |> Macro.var(nil)}}
end
end
def field_build_anon_var({{field, _value}, index})
when is_atom(field) and is_integer(index) do
{:ok, {field, :_ |> Macro.var(nil)}}
end
def cpo_resolve_guard_field_match(cpo, ctrl \\ [])
def cpo_resolve_guard_field_match(cpo, ctrl) do
with {:ok, {field_vars, field_tuples}} <- cpo |> cpo_resolve_guard_field_values(ctrl) do
match_args = {:%{}, [], field_vars}
match_form = {:%, [], [{:__MODULE__, [], nil}, match_args]}
# convenience: include the first field var tuple explicitly
{:ok, {field_vars |> hd, field_vars, field_tuples, match_form}}
else
{:error, %{__exception__: true}} = result -> result
end
end
def opts_resolve_opts_fun_args(opts) do
with {:ok, opts} <- opts |> opts_normalise do
cond do
# if fun_args explicitly given, must be correct!
Keyword.has_key?(opts, @plymio_codi_key_fun_args) ->
with {:ok, fun_args} <- opts |> cpo_resolve_fun_args do
{:ok, fun_args}
else
{:error, %{__exception__: true}} = result -> result
end
true ->
# this will look for arity
with {:ok, fun_args} <- opts |> cpo_resolve_fun_args do
{:ok, [Macro.var(:opts, nil) | fun_args |> Enum.slice(1..-1)]}
else
{:error, %{__exception__: true}} = result -> result
end
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_bang_module(cpo, ctrl \\ []) do
with {:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_module_key(@plymio_codi_key_bang_module),
{:ok, _} = result <- cpo |> cpo_resolve_fun_module(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_bang_name(cpo, ctrl \\ []) do
with {:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_name_key(@plymio_codi_key_bang_name),
{:ok, _} = result <- cpo |> cpo_resolve_fun_name(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_bang_args(cpo, ctrl \\ []) do
with {:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_args_key(@plymio_codi_key_bang_args),
{:ok, ctrl} <- ctrl |> ctrl_put_fun_arity_key(@plymio_codi_key_bang_arity),
{:ok, _} = result <- cpo |> cpo_resolve_fun_args(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_bang_doc(cpo, ctrl \\ []) do
with {:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_doc_key(@plymio_codi_key_bang_doc),
{:ok, _} = result <- cpo |> cpo_resolve_fun_doc(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_query_module(cpo, ctrl \\ []) do
with {:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_module_key(@plymio_codi_key_query_module),
{:ok, _} = result <- cpo |> cpo_resolve_fun_module(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_query_name(cpo, ctrl \\ []) do
with {:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_name_key(@plymio_codi_key_query_name),
{:ok, _} = result <- cpo |> cpo_resolve_fun_name(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_query_args(cpo, ctrl \\ []) do
with {:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_args_key(@plymio_codi_key_query_args),
{:ok, ctrl} <- ctrl |> ctrl_put_fun_arity_key(@plymio_codi_key_query_arity),
{:ok, _} = result <- cpo |> cpo_resolve_fun_args(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_query_doc(cpo, ctrl \\ []) do
with {:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_doc_key(@plymio_codi_key_query_doc),
{:ok, _} = result <- cpo |> cpo_resolve_fun_doc(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_delegate_module(cpo, ctrl \\ []) do
with {:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_module_key(@plymio_codi_key_delegate_module),
{:ok, _} = result <- cpo |> cpo_resolve_fun_module(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_delegate_name(cpo, ctrl \\ []) do
with {:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_name_key(@plymio_codi_key_delegate_name),
{:ok, _} = result <- cpo |> cpo_resolve_fun_name(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_delegate_args(cpo, ctrl \\ []) do
with {:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_args_key(@plymio_codi_key_delegate_args),
{:ok, ctrl} <- ctrl |> ctrl_put_fun_arity_key(@plymio_codi_key_delegate_arity),
{:ok, _} = result <- cpo |> cpo_resolve_fun_args(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_delegate_doc(cpo, ctrl \\ []) do
with {:ok, ctrl} <- ctrl |> ctrl_normalise,
{:ok, ctrl} <- ctrl |> ctrl_put_fun_doc_key(@plymio_codi_key_delegate_doc),
{:ok, _} = result <- cpo |> cpo_resolve_fun_doc(ctrl) do
result
else
{:error, %{__exception__: true}} = result -> result
end
end
def resolve_fun_args(args) do
args
|> case do
x when is_positive_integer(x) ->
# this will generate a list var1, var2, etc
x
x when is_negative_integer(x) ->
# this will generate a list var1, var2, etc
0 - x
x when is_value_unset_or_nil(x) ->
[]
x ->
x |> List.wrap()
end
|> normalise_vars
end
def resolve_type_args(args) do
args
|> case do
x when is_positive_integer(x) ->
# this will generate a list of vars all called any
0 - x
x when is_negative_integer(x) ->
# this will generate a list of vars all called any
x
x when is_value_unset_or_nil(x) ->
[]
x ->
x |> List.wrap()
end
|> normalise_vars
end
def cpo_resolve_typespec_spec_name(cpo) do
with {:ok, cpo} <- cpo |> opts_validate do
cpo
|> Keyword.has_key?(@plymio_codi_key_typespec_spec_name)
|> case do
true ->
cpo |> cpo_get_typespec_spec_name
_ ->
new_error_result(m: "cpo type name missing", v: cpo)
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_typespec_spec_args(opts, default \\ @plymio_fontais_the_unset_value) do
with {:ok, opts} <- opts |> opts_validate do
cond do
Keyword.has_key?(opts, @plymio_codi_key_typespec_spec_args) ->
with {:ok, type_args} <- opts |> opts_get(@plymio_codi_key_typespec_spec_args),
{:ok, _} = result <- type_args |> resolve_type_args do
result
else
{:error, %{__exception__: true}} = result -> result
end
Keyword.has_key?(opts, @plymio_codi_key_typespec_spec_arity) ->
with {:ok, type_arity} <- opts |> opts_get(@plymio_codi_key_typespec_spec_arity),
{:ok, _type_arity} = result <- type_arity |> resolve_type_args do
result
else
{:error, %{__exception__: true}} = result -> result
end
Keyword.has_key?(opts, @plymio_codi_key_fun_arity) ->
with {:ok, fun_arity} <- opts |> opts_get(@plymio_codi_key_fun_arity) do
fun_arity
|> case do
x when is_positive_integer(x) ->
0 - x
x ->
0 - length(x |> List.wrap())
end
|> resolve_type_args
else
{:error, %{__exception__: true}} = result -> result
end
Keyword.has_key?(opts, @plymio_codi_key_fun_args) ->
with {:ok, fun_args} <- opts |> opts_get(@plymio_codi_key_fun_args),
{:ok, fun_args} <- fun_args |> resolve_fun_args,
{:ok, _type_args} = result <- fun_args |> length |> resolve_type_args do
result
else
{:error, %{__exception__: true}} = result -> result
end
true ->
default
|> resolve_type_args
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_typespec_spec_result(opts) do
with {:ok, opts} <- opts |> opts_validate,
{:ok, type_result} <- opts |> opts_get(@plymio_codi_key_typespec_spec_result, []) do
type_result
|> case do
# only really useful if is 1 i.e. the result is any
x when is_positive_integer(x) ->
(0 - x) |> normalise_vars
x ->
x |> normalise_type_arg
end
|> case do
{:error, %{__struct__: _}} = result ->
result
{:ok, [form]} ->
form |> form_validate
{:ok, forms} when is_list(forms) ->
with {:ok, forms} <- forms |> forms_normalise do
form =
forms
|> Enum.reverse()
|> Enum.reduce(fn f, s ->
quote do
unquote(f) | unquote(s)
end
end)
{:ok, form}
else
{:error, %{__exception__: true}} = result -> result
end
{:ok, form} ->
form |> form_validate
end
else
{:error, %{__exception__: true}} = result -> result
end
end
defp option_getset_default_fun_namer(opts) do
with {:ok, opts} <- opts |> opts_validate do
opts
|> Keyword.has_key?(@plymio_codi_key_fun_name)
|> case do
true ->
opts |> opts_get(@plymio_codi_key_fun_name)
_ ->
with {:ok, pattern_name} <- opts |> opts_fetch(@plymio_codi_key_pattern),
{:ok, fun_key} <- opts |> opts_fetch(@plymio_codi_key_fun_key) do
{:ok, "#{to_string(pattern_name)}_#{to_string(fun_key)}" |> String.to_atom()}
else
{:error, %{__exception__: true}} = result -> result
end
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def cpo_resolve_fun_namer(opts) do
with {:ok, opts} <- opts |> opts_validate do
opts
|> Keyword.get(@plymio_codi_key_fun_namer, &option_getset_default_fun_namer/1)
|> case do
fun_namer when is_function(fun_namer, 1) ->
namer = fn opts ->
opts
|> fun_namer.()
|> case do
{:error, %{__exception__: true}} = result -> result
{:ok, _} = result -> result
fun_name -> {:ok, fun_name}
end
|> case do
{:error, %{__exception__: true}} = result ->
result
{:ok, fun_name} ->
fun_name
|> case do
x when is_atom(x) ->
opts |> opts_put(@plymio_codi_key_fun_name, x)
x when is_binary(x) ->
opts |> opts_put(@plymio_codi_key_fun_name, x |> String.to_atom())
x ->
new_error_result(m: "function namer result invalid", v: x)
end
end
end
{:ok, namer}
x ->
new_error_result(m: "function namer function invalid", v: x)
end
else
{:error, %{__exception__: true}} = result -> result
end
end
def opts_resolve_proxy_names(opts) do
with {:ok, opts} <- opts |> opts_validate do
opts
|> Keyword.fetch(@plymio_codi_key_proxy_name)
|> case do
{:ok, proxy_name} ->
proxy_name |> normalise_proxy_names
:error ->
new_error_result(m: "opts function proxy name missing", v: opts)
end
else
{:error, %{__exception__: true}} = result -> result
end
end
defp normalise_proxy_names(names) do
names
|> List.wrap()
|> validate_proxy_names
end
defp validate_proxy_names(names)
defp validate_proxy_names([]) do
{:ok, []}
end
defp validate_proxy_names(names) when is_list(names) do
names
|> Enum.split_with(&is_atom/1)
|> case do
{names, []} ->
{:ok, names}
{_, invalid_names} ->
new_error_result(m: "proxy names invalid", v: invalid_names)
end
end
defp resolve_function_sig(m, f, a)
defp resolve_function_sig(m, f, a)
when m in [
Plymio.Fontais.Option
] and a == 2 and
f in [
:opts_get,
:opts_fetch
] do
[opts: 0, key: 1]
end
defp resolve_function_sig(m, f, a)
when m in [
Plymio.Fontais.Option
] and f == :opts_get and a == 3 do
[opts: 0, key: 1, default: 2]
end
defp resolve_function_sig(_m, _f, a) do
a
|> Macro.generate_arguments(nil)
|> Enum.with_index()
end
def opts_create_fun_sig(opts) do
with {:ok, opts} <- opts |> opts_normalise,
{:ok, pattern} <- opts |> opts_fetch(@plymio_codi_key_pattern),
{:ok, fun_module} <- opts |> cpo_resolve_fun_module,
{:ok, fun_arity} <- opts |> opts_fetch(@plymio_codi_key_fun_arity) do
fun_sig_base =
0..(fun_arity - 1)
|> Enum.map(fn index -> {"var#{to_string(index)}" |> String.to_atom(), index} end)
fun_sig = resolve_function_sig(fun_module, pattern, fun_arity)
fun_sig =
(fun_sig_base ++ fun_sig)
|> Enum.group_by(fn {_k, v} -> v end)
|> Enum.map(fn {_v, kvs} -> kvs |> List.last() end)
|> Enum.into(%{})
opts |> opts_put(@plymio_codi_key_sig, fun_sig)
else
{:error, %{__exception__: true}} = result -> result
end
end
def validate_module_dict(module_dict)
def validate_module_dict(dict) when is_map(dict) do
with {:ok, _} <- dict |> Map.keys() |> validate_fun_modules,
{:ok, _} <- dict |> Map.values() |> opzioni_validate do
{:ok, dict}
else
{:error, %{__exception__: true} = error} ->
new_error_result(m: "module dictionary invalid", v: error)
end
end
def validate_module_dict(dict) do
new_error_result(m: "module dictionary invalid", v: dict)
end
# validate since is a semver spec
def validate_since(since)
def validate_since(since) when is_binary(since) do
since
|> Version.parse()
|> case do
{:ok, _} ->
{:ok, since}
_ ->
new_error_result(m: "since invalid", v: since)
end
end
def validate_since(since) do
new_error_result(m: "since invalid", v: since)
end
def validate_deprecated(deprecated) do
deprecated
|> case do
x when is_binary(x) ->
{:ok, x}
_ ->
new_error_result(m: "deprecated invalid", v: deprecated)
end
end
end