Packages

Shapex is a library for validating data structures in Elixir.

Current section

Files

Jump to
shapex lib types integer.ex
Raw

lib/types/integer.ex

defmodule Shapex.Types.Integer do
@moduledoc """
This module provides a type for validating integers.
Existing validations:
- :gt - greater than
- :gte - greater than or equal to
- :lt - less than
- :lte - less than or equal to
- :eq - equal
- :neq - not equal
- :in - checks if the value is in a list
- :not_in - checks if the value is not in a list
It's better to use the Shapex.Types.integer/1 function to create a schema,
since it covers implementation details and provides a more user-friendly API.
"""
@behaviour Shapex.Type
@type target(a) :: a | {a, String.t()}
@type validation_rules ::
{:gt, target(integer())}
| {:gte, target(integer())}
| {:lt, target(integer())}
| {:lte, target(integer())}
| {:eq, target(integer())}
| {:neq, target(integer())}
| {:in, target(list(integer()))}
| {:not_in, target(list(integer()))}
@type t :: %__MODULE__{
validations: [validation_rules()]
}
defstruct [:validations]
def validate(_, value) when not is_integer(value),
do: {:error, %{type: "Value must be an integer"}}
@impl Shapex.Type
def validate(%__MODULE__{} = schema, value) do
validation_results =
Enum.reduce(schema.validations, %{}, fn
{:custom, {rule_name, callback}}, errors ->
case callback.(value) do
nil -> errors
error -> Map.put(errors, rule_name, error)
end
{rule_name, {target, message}}, errors ->
case do_validate(rule_name, target, value, message) do
nil -> errors
error -> Map.put(errors, rule_name, error)
end
{rule_name, target}, errors ->
case do_validate(rule_name, target, value) do
nil -> errors
error -> Map.put(errors, rule_name, error)
end
end)
if validation_results == %{} do
{:ok, :valid}
else
{:error, validation_results}
end
end
defp do_validate(rule_name, target, value, message \\ nil)
defp do_validate(:gt, target, value, message) when not (value > target) do
message || "Value must be greater than #{target}"
end
defp do_validate(:gte, target, value, message) when not (value >= target) do
message || "Value must be greater than or equal to #{target}"
end
defp do_validate(:lt, target, value, message) when not (value < target) do
message || "Value must be less than #{target}"
end
defp do_validate(:lte, target, value, message) when not (value <= target) do
message || "Value must be less than or equal to #{target}"
end
defp do_validate(:eq, target, value, message) when not (value == target) do
message || "Value must be equal to #{target}"
end
defp do_validate(:neq, target, value, message) when not (value != target) do
message || "Value must not be equal to #{target}"
end
defp do_validate(:in, range, value, message) do
if not Enum.member?(range, value) do
message || "Value must be one of #{inspect(range)}"
end
end
defp do_validate(:not_in, range, value, message) do
if Enum.member?(range, value) do
message || "Value must not be one of #{inspect(range)}"
end
end
defp do_validate(_rule_name, _target, _value, _message) do
nil
end
end