Current section
Files
Jump to
Current section
Files
lib/rules.ex
defmodule Request.Validator.Rules do
defmodule Bail do
defstruct rules: []
@type t :: %__MODULE__{rules: list(atom() | tuple())}
end
defmodule Map_ do
defstruct attrs: []
@type t :: %__MODULE__{attrs: maybe_improper_list()}
end
defmodule Array do
defstruct attrs: []
@type t :: %__MODULE__{attrs: list(atom() | tuple())}
end
@spec bail(list(atom() | tuple())) :: Request.Validator.Rules.Bail.t()
def bail(rules), do: %__MODULE__.Bail{rules: rules}
@spec map(maybe_improper_list()) :: Request.Validator.Rules.Map_.t()
def map(attrs), do: %__MODULE__.Map_{attrs: attrs}
@spec array(maybe_improper_list) :: Request.Validator.Rules.Array.t()
def array(attrs) when is_list(attrs), do: %__MODULE__.Array{attrs: attrs}
defmacro __using__(_opts) do
quote location: :keep do
def email(value, opts \\ [])
def email(nil, _), do: :ok
def email(value, _) do
validate(EmailChecker.valid?(value || ""), "This field must be a valid email address.")
end
def required(value, opts \\ [])
def required(value, _) when is_boolean(value), do: :ok
def required(value, _) do
result =
case value_size(value) do
nil ->
false
length ->
length > 0
end
validate(result, "This field is required.")
end
def string(value, opts \\ [])
def string(nil, _), do: :ok
def string(value, _) do
validate(is_binary(value), "This field must be a string.")
end
def numeric(value, opts \\ [])
def numeric(nil, _), do: :ok
def numeric(value, _) do
validate(is_number(value), "This field must be a number.")
end
def map(value, opts \\ [])
def map(value, _) when is_nil(value) or is_map(value), do: :ok
def map(value, _), do: {:error, "This field is expected to be a map."}
def in_list(value, list, opts \\ [])
def in_list(value, list, _) do
validate(Enum.member?(list, value), "This field is invalid.")
end
def max(value, boundary, opts \\ [])
def max(nil, _, _), do: :ok
def max(value, boundary, _opts) do
msg =
cond do
is_binary(value) ->
"This field must be less than or equal #{boundary} characters."
is_list(value) ->
"This field must be less than or equal #{boundary} items."
true ->
"This field must be less than or equal #{boundary}."
end
validate(value_size(value) <= boundary, msg)
end
def min(value, boundary, opts \\ [])
def min(nil, _, _), do: :ok
def min(value, boundary, _opts) do
msg =
cond do
is_binary(value) ->
"This field must be at least #{boundary} characters."
is_list(value) ->
"This field must be at least #{boundary} items."
true ->
"This field must be at least #{boundary}."
end
validate(value_size(value) >= boundary, msg)
end
def gt(nil, _, _), do: :ok
def gt(value, boundary, opts) do
other_field =
opts
|> Keyword.get(:fields)
|> Map.get(to_string(boundary))
case same_type(value, other_field) do
true ->
msg =
cond do
is_list(value) ->
"This field must have more than #{value_size(other_field)} items."
is_binary(value) ->
"This field must have more than #{value_size(other_field)} characters."
true ->
"This field must be greater than #{value_size(other_field)}."
end
validate(value_size(value) > value_size(other_field), msg)
false ->
{:error, "This field and #{boundary} has to be of same type."}
end
end
def lt(nil, _, _), do: :ok
def lt(value, boundary, opts) do
other_field =
opts
|> Keyword.get(:fields)
|> Map.get(to_string(boundary))
case same_type(value, other_field) do
true ->
msg =
cond do
is_list(value) ->
"This field must have less than #{value_size(other_field)} items."
is_binary(value) ->
"This field must have less than #{value_size(other_field)} characters."
true ->
"This field must be less than #{value_size(other_field)}."
end
validate(value_size(value) < value_size(other_field), msg)
false ->
{:error, "This field and #{boundary} has to be of same type."}
end
end
def confirmed(value, field: field, fields: fields) do
path = "#{field}_confirmation"
validate(value == fields[path], "This field confirmation does not match.")
end
def size(value, boundary, opts \\ [])
def size(value, boundary, _) do
msg =
cond do
is_list(value) ->
"This field must contain #{boundary} items."
is_binary(value) ->
"This field must be #{boundary} characters."
true ->
"This field must be #{boundary}."
end
validate(value_size(value) === boundary, msg)
end
def boolean(value, opts \\ [])
def boolean(value, _) when is_number(value) and value in [0, 1], do: :ok
def boolean(value, _) when is_binary(value) and value in ~w[0 1], do: :ok
def boolean(value, _), do: validate(is_boolean(value), "This field must be true or false")
defp value_size(value) when is_number(value), do: value
defp value_size(value) when is_list(value) or is_map(value), do: Enum.count(value)
defp value_size(value) when is_binary(value), do: String.length(value)
defp value_size(_value), do: nil
defp same_type(value1, value2) when is_number(value1) and is_number(value2), do: true
defp same_type(value1, value2) when is_binary(value1) and is_binary(value2), do: true
defp same_type(value1, value2) when is_list(value1) and is_list(value2), do: true
defp same_type(_value1, _value2), do: false
defp validate(condition, msg) do
if !condition do
{:error, msg}
else
:ok
end
end
end
end
end