Current section

Files

Jump to
argx lib use.ex
Raw

lib/use.ex

defmodule Argx.Defconfig.Use do
@moduledoc false
alias Argx.Checker, as: C
alias Argx.Const
alias Argx.Defconfig.Use, as: Self
alias Argx.Parser, as: P
defmacro __using__(_opts) do
quote do
defmacro defconfig(name, configs) do
C.check_defconfig!(name, configs)
name = P.parse_defconfig_name(name)
configs = P.parse_configs(configs)
attr = %{name => configs} |> Macro.escape()
quote do
unquote(Self.reg_attr())
Module.put_attribute(
__MODULE__,
unquote(Const.defconfigs_key()),
unquote(attr)
)
end
end
end
end
def reg_attr do
quote do
Module.register_attribute(
__MODULE__,
unquote(Const.defconfigs_key()),
accumulate: true,
persiste: true
)
end
end
end
defmodule Argx.WithCheck.Use do
@moduledoc false
alias Argx.Checker, as: C
alias Argx.Const
alias Argx.Formatter, as: F
alias Argx.Matcher, as: M
alias Argx.Parser, as: P
alias Argx.Util, as: U
alias Argx.WithCheck.Use, as: Self
defmacro __using__(_opts) do
quote do
@defconfigs {:@, [], [{Const.defconfigs_key(), [], nil}]}
defmacro with_check(configs, do: block) do
C.check!(configs, block)
%{
f: f,
a: a,
guard: guard,
block: block
} = P.parse_fun(block)
real_f_name = Self.make_real_f_name(f)
use_m = __MODULE__
quote do
# ignore undefined module attribute warning
unquote(Self.reg_attr())
def unquote(f)(unquote_splicing(a)) when unquote(guard) do
args = unquote(Self.make_args(a))
configs = unquote(Self.merge_configs(configs, @defconfigs))
__MODULE__
|> M.match(unquote(f), args, configs)
|> Self.post_match(unquote(use_m), __MODULE__, unquote(real_f_name))
end
def unquote(real_f_name)(unquote_splicing(a)) do
unquote(block)
end
end
end
end
end
###
def post_match({:error, _} = err, use_m, current_m, _) do
err |> F.format_errors(use_m, current_m)
end
def post_match([_ | _] = new_args, _, current_m, real_f_name) do
apply(current_m, real_f_name, new_args)
end
###
def make_args(a) do
quote do
keys = unquote(a |> get_arg_names([]))
values = [unquote_splicing(a)]
keys |> Self.do_make_args(values, [])
end
end
def do_make_args([], [], acc) do
acc |> Enum.reverse()
end
def do_make_args([_ | _], [], acc) do
acc
end
def do_make_args([], [_ | _], acc) do
acc
end
def do_make_args([key | k_rest], [value | v_rest], acc) do
k_rest |> do_make_args(v_rest, Keyword.put(acc, key, value))
end
defp get_arg_names([], acc) do
acc |> Enum.reverse()
end
defp get_arg_names([{arg, _, _} | rest], acc) do
rest |> get_arg_names([arg | acc])
end
defp get_arg_names([_ | rest], acc) do
rest |> get_arg_names(acc)
end
###
def merge_configs(configs, defconfigs) do
quote do
{names, configs} =
unquote(configs |> P.parse_configs() |> Macro.escape())
|> Map.pop(unquote(Const.names_key()))
names
|> Self.get_configs_by_names(unquote(defconfigs))
|> Map.merge(configs)
end
end
def get_configs_by_names([_ | _] = names, defconfigs) do
names
|> Enum.reduce(%{}, fn name, acc ->
configs = defconfigs |> U.list_to_map() |> Map.get(name, nil)
(configs && acc |> Map.merge(configs)) || acc
end)
end
def get_configs_by_names(_, _) do
%{}
end
###
def make_real_f_name(f) do
U.make_fun_name("real", f)
end
def reg_attr do
quote do
Module.register_attribute(
__MODULE__,
unquote(Const.defconfigs_key()),
accumulate: true,
persiste: true
)
end
end
end