Current section
Files
Jump to
Current section
Files
lib/validator/all_equal.ex
defmodule Funx.Validator.AllEqual do
@moduledoc """
Validates that all elements in a list are equal to each other.
## Maybe Support
This validator supports `Maybe` types from Prism projections:
- `Nothing` - Passes validation (optional fields without values)
- `Just(list)` - Validates the list inside the Just
This makes the validator compatible with optional fields in the validation DSL.
## Optional Options
- `:message` - Custom error message callback `(value -> String.t())`
- `:eq` - Custom equality comparator (defaults to `Funx.Eq.Protocol`)
## Examples
iex> Funx.Validator.AllEqual.validate([1, 1, 1], [])
%Funx.Monad.Either.Right{right: [1, 1, 1]}
iex> Funx.Validator.AllEqual.validate([1, 2, 3], [])
%Funx.Monad.Either.Left{left: %Funx.Errors.ValidationError{errors: ["must be all matching"]}}
iex> Funx.Validator.AllEqual.validate("not a list", [])
%Funx.Monad.Either.Left{left: %Funx.Errors.ValidationError{errors: ["must be a list"]}}
iex> Funx.Validator.AllEqual.validate([1, 2], [message: fn _ -> "all items must match" end])
%Funx.Monad.Either.Left{left: %Funx.Errors.ValidationError{errors: ["all items must match"]}}
## Using Custom Equality
You can provide a custom equality comparator via the `:eq` option:
iex> case_insensitive = %{
...> eq?: fn a, b -> String.downcase(a) == String.downcase(b) end,
...> not_eq?: fn a, b -> String.downcase(a) != String.downcase(b) end
...> }
iex> Funx.Validator.AllEqual.validate(["HELLO", "hello", "HeLLo"], [eq: case_insensitive])
%Funx.Monad.Either.Right{right: ["HELLO", "hello", "HeLLo"]}
## Maybe Examples
iex> Funx.Validator.AllEqual.validate(%Funx.Monad.Maybe.Nothing{}, [])
%Funx.Monad.Either.Right{right: %Funx.Monad.Maybe.Nothing{}}
iex> Funx.Validator.AllEqual.validate(%Funx.Monad.Maybe.Just{value: [1, 1, 1]}, [])
%Funx.Monad.Either.Right{right: [1, 1, 1]}
iex> Funx.Validator.AllEqual.validate(%Funx.Monad.Maybe.Just{value: [1, 2]}, [])
%Funx.Monad.Either.Left{left: %Funx.Errors.ValidationError{errors: ["must be all matching"]}}
"""
use Funx.Validator
alias Funx.List
@impl Funx.Validator
def valid?(list, opts, _env) when is_list(list) do
eq = Keyword.get(opts, :eq, Funx.Eq.Protocol)
all_elements_match?(list, eq)
end
def valid?(_non_list, _opts, _env), do: false
@impl Funx.Validator
def default_message(value, _opts) when is_list(value) do
"must be all matching"
end
def default_message(_value, _opts) do
"must be a list"
end
defp all_elements_match?(list, eq) do
unique_count = list |> List.uniq(eq) |> length()
unique_count == 1
end
end