Current section
Files
Jump to
Current section
Files
lib/validator/range.ex
defmodule Funx.Validator.Range do
@moduledoc """
Validates that a number falls within an inclusive range.
## Optional Options
- `:min` - Minimum value (inclusive)
- `:max` - Maximum value (inclusive)
- `:message` - Custom error message callback `(value -> String.t())`
At least one of `:min` or `:max` must be provided.
## Examples
iex> Funx.Validator.Range.validate(5, min: 1, max: 10)
%Funx.Monad.Either.Right{right: 5}
iex> Funx.Validator.Range.validate(15, min: 1, max: 10)
%Funx.Monad.Either.Left{left: %Funx.Errors.ValidationError{errors: ["must be between 1 and 10"]}}
iex> Funx.Validator.Range.validate(5, min: 10)
%Funx.Monad.Either.Left{left: %Funx.Errors.ValidationError{errors: ["must be at least 10"]}}
iex> Funx.Validator.Range.validate(15, max: 10)
%Funx.Monad.Either.Left{left: %Funx.Errors.ValidationError{errors: ["must be at most 10"]}}
"""
use Funx.Validator
@impl Funx.Validator
def valid?(number, opts, _env) when is_number(number) do
min = Keyword.get(opts, :min)
max = Keyword.get(opts, :max)
if is_nil(min) and is_nil(max) do
raise ArgumentError, "Range validator requires at least :min or :max option"
end
in_range?(number, min, max)
end
def valid?(_non_number, _opts, _env), do: false
@impl Funx.Validator
def default_message(value, opts) when is_number(value) do
min = Keyword.get(opts, :min)
max = Keyword.get(opts, :max)
build_range_message(min, max)
end
def default_message(_value, _opts) do
"must be a number"
end
defp in_range?(value, nil, max), do: value <= max
defp in_range?(value, min, nil), do: value >= min
defp in_range?(value, min, max), do: value >= min and value <= max
defp build_range_message(min, max) when not is_nil(min) and not is_nil(max) do
"must be between #{min} and #{max}"
end
defp build_range_message(min, nil) when not is_nil(min) do
"must be at least #{min}"
end
defp build_range_message(nil, max) when not is_nil(max) do
"must be at most #{max}"
end
end