defmodule Fussy.Validators.Either do
@behaviour Fussy.Validator
alias Fussy.Utils
alias Fussy.ValidationError
defstruct [:variants]
@opaque t :: %__MODULE__{}
@spec new(list(Fussy.Validator.t())) :: __MODULE__.t()
def new([]), do: raise(ArgumentError, "must provide at least one variant")
def new(variants) when is_list(variants) do
%__MODULE__{variants: variants}
end
def validate(%__MODULE__{} = v, term), do: validate(v, [], term)
@impl true
def validate(%__MODULE__{variants: variants}, path, term) do
variants
|> Enum.find_value(fn v ->
case Utils.validate_using(v, term) do
{:ok, valid_term} ->
{:ok, valid_term}
{:error, _} ->
nil
end
end)
|> then(fn
{:ok, valid_term} -> {:ok, valid_term}
nil -> error(path, term)
end)
end
defp error(path, term) do
{:error,
[
%ValidationError{
mod: __MODULE__,
path: path,
msg: "must be one of the defined variants",
term: term
}
]}
end
end