lib/fussy/validators/either.ex

defmodule Fussy.Validators.Either do
  @behaviour Fussy.Validator

  alias Fussy.Utils

  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
    struct!(%__MODULE__{}, variants: variants)
  end

  @impl true
  def validate(%__MODULE__{variants: variants}, term) do
    variants
    |> Enum.find_value(
      {:error, ["must be one of the defined variants"]},
      fn variant ->
        case Utils.validate_using(variant, term) do
          {:ok, valid_term} ->
            {:ok, valid_term}

          {:error, _} ->
            nil
        end
      end
    )
  end
end