Current section

Files

Jump to
croma lib croma validation.ex
Raw

lib/croma/validation.ex

defmodule Croma.Validation do
@moduledoc """
Module for code generation of argument validation (see `Croma.Defun.defun/2`).
This module is intended for internal use.
"""
def make(type_expr, v, caller) do
ast = validation_expr(type_expr, v, caller)
{name, _, _} = v
type_string = Macro.to_string(type_expr)
quote bind_quoted: [name: name, ast: ast, type_string: type_string] do
case ast do
true -> nil
false -> raise "validation error: #{Atom.to_string(name)} is not a valid #{type_string}"
end
end
end
defp validation_expr(type_expr, v, caller) do
case type_expr do
a when is_atom(a) -> validation_expr_equal(v, a)
l when is_list(l) -> validation_expr_module(v, Croma.List)
{_, _} -> validation_expr_module(v, Croma.Tuple)
{:t, _, _} -> validation_expr_module(v, caller.module)
{:|, _, [t1, t2]} -> validation_expr_union(v, t1, t2, caller)
{{:., _, [mod_alias, :t]}, _, _} -> validation_expr_module(v, replace_elixir_type_module(mod_alias, caller))
{first, _, _} -> validation_expr_module(v, module_for(first, type_expr))
_ -> error(type_expr)
end
end
defp module_for(first, type_expr) do
case first do
:integer -> Croma.Integer
:pos_integer -> Croma.PosInteger
:neg_integer -> Croma.NeInteger
:non_neg_integer -> Croma.NonNegInteger
:boolean -> Croma.Boolean
:byte -> Croma.Byte
:char -> Croma.Char
:float -> Croma.Float
:number -> Croma.Number
:binary -> Croma.Binary
:bitstring -> Croma.BitString
:module -> Croma.Atom
:atom -> Croma.Atom
:node -> Croma.Atom
:fun -> Croma.Function
:pid -> Croma.Pid
:port -> Croma.Port
:reference -> Croma.Reference
:char_list -> Croma.List
:list -> Croma.List
:map -> Croma.Map
:tuple -> Croma.Tuple
:%{} -> Croma.Map
:{} -> Croma.Tuple
:<<>> -> Croma.BitString
_ -> error(type_expr)
end
end
defp validation_expr_module(v, mod) do
quote bind_quoted: [v: v, mod: mod] do
mod.valid?(v)
end
end
defp validation_expr_equal(v, value) do
quote bind_quoted: [v: v, value: value] do
v == value
end
end
defp validation_expr_union(v, t1, t2, caller) do
q1 = validation_expr(t1, v, caller)
q2 = validation_expr(t2, v, caller)
quote do
unquote(q1) || unquote(q2)
end
end
defp replace_elixir_type_module(mod_alias, caller) do
mod = Macro.expand(mod_alias, caller)
case mod do
String -> Croma.String
_ -> mod
end
end
defp error(type_expr) do
raise "cannot generate validation code for the given type: #{Macro.to_string(type_expr)}"
end
end