Current section

Files

Jump to
argx lib checker.ex
Raw

lib/checker.ex

defmodule Argx.Checker do
@moduledoc false
@allowed_types [:list, :map, :string, :integer, :float]
@allowed_functionalities [:optional, :auto]
@allowed_fun_types [:def, :defp]
###
def some_type?(v, :integer), do: is_integer(v)
def some_type?(v, :float), do: is_float(v)
def some_type?(v, :string), do: is_bitstring(v)
def some_type?(v, :list), do: is_list(v)
def some_type?(v, :map), do: is_map(v)
def some_type?(_, _), do: false
def in_range?(v, [l, r], :integer) when is_integer(v) do
(v > l and v < r) or (v == l and v == r)
end
def in_range?(v, [l, r], :float) when is_float(v) do
(v > l and v < r) or (v == l and v == r)
end
def in_range?(v, [l, r], :string) when is_bitstring(v) do
len = String.length(v)
(len > l and len < r) or (len == l and len == r)
end
def in_range?(v, [l, r], :list) when is_list(v) do
len = length(v)
(len > l and len < r) or (len == l and len == r)
end
def in_range?(v, [l, r], :map) when is_map(v) do
len = map_size(v)
(len > l and len < r) or (len == l and len == r)
end
def in_range?(_, _, _) do
false
end
def are_keys_equal!(f, [_ | _] = args, %{} = configs) when is_atom(f) do
keys1 = args |> Keyword.keys() |> Enum.sort()
keys2 = configs |> Map.keys() |> Enum.sort()
if keys1 == keys2 do
:ignore
else
raise Argx.Error, "#{f} function has arg not config"
end
end
###
def check_defconfig!(name, config) do
check_config_name!(name)
config |> extract_config!(true)
end
def check!(configs, block) do
check_configs!(configs)
check_fun_block!(block)
end
###
defp check_configs!({:configs, _, configs}) do
configs |> extract_config!(true)
end
defp check_configs!(_) do
raise Argx.Error, "syntax error: not found configs keyword"
end
defp check_fun_block!({:__block__, _, []}) do
raise Argx.Error, "with_check block is empty"
end
defp check_fun_block!({:__block__, _, [{f_type1, _, _} | [{f_type2, _, _} | _]]})
when f_type1 in @allowed_fun_types or f_type2 in @allowed_fun_types do
raise Argx.Error, "only support one function"
end
defp check_fun_block!(_block) do
:ok
end
###
defp extract_config!([], first?) do
if first? do
raise Argx.Error, "config content is empty"
else
:ok
end
end
defp extract_config!([config | rest], _first?) do
config |> extract_config!() |> every_config!(false)
rest |> extract_config!(false)
end
defp extract_config!(config, _) do
config |> extract_config!() |> every_config!(false)
end
defp extract_config!({:||, _, [{_field, _, [_ | _] = config}, _default]}) do
config
end
defp extract_config!({:__aliases__, _, _} = defconfig_name) do
defconfig_name
end
defp extract_config!({_field, _, [_ | _] = config}) do
config
end
defp extract_config!(_) do
raise Argx.Error, "invalid defconfig"
end
defp every_config!({:__aliases__, _, _} = defconfig_name, _) do
defconfig_name |> check_config_name!()
end
defp every_config!([config | rest], _has_type?) when config in @allowed_types do
rest |> every_config!(true)
end
defp every_config!([config | rest], has_type?) when config in @allowed_functionalities do
rest |> every_config!(has_type?)
end
defp every_config!([{:.., _, [l, r]} | rest], has_type?) when is_integer(l) and is_integer(r) do
rest |> every_config!(has_type?)
end
defp every_config!([value | rest], has_type?) when is_integer(value) do
rest |> every_config!(has_type?)
end
defp every_config!([config | _rest], _) do
err_msg =
case _to_string(config) do
:error ->
"invalid defconfig"
_ ->
"unknown #{_to_string(config)} defconfig"
end
raise Argx.Error, err_msg
end
defp every_config!([], has_type?) do
if has_type? do
:ok
else
raise Argx.Error, "not found one of #{inspect(@allowed_types)} config items"
end
end
defp every_config!(config, _) when is_atom(config) do
:ok
end
###
defp check_config_name!({:__aliases__, _, [name]}) when is_atom(name) do
:ok
end
defp check_config_name!(_) do
raise Argx.Error, "invalid defconfig name, like: NameYes"
end
###
defp _to_string(value) when is_atom(value) do
":#{value}"
end
defp _to_string(value) when is_bitstring(value) do
value
end
defp _to_string(_) do
:error
end
end