lib/fussy/validators/boolean.ex

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

  alias Fussy.ValidationError

  defstruct strict: true

  @opaque t :: %__MODULE__{}

  @spec new(strict: boolean()) :: __MODULE__.t()
  def new(opts \\ []), do: struct!(%__MODULE__{}, opts)

  def validate(opts, term), do: validate(opts, [], term)

  @impl true
  def validate(%__MODULE__{}, _path, term) when is_boolean(term), do: {:ok, term}

  @impl true
  def validate(%__MODULE__{strict: false}, _path, 0), do: {:ok, false}

  @impl true
  def validate(%__MODULE__{strict: false}, _path, term) when is_integer(term), do: {:ok, true}

  @impl true
  def validate(%__MODULE__{strict: false}, path, term) when is_binary(term) do
    case String.downcase(term) do
      "true" ->
        {:ok, true}

      "false" ->
        {:ok, false}

      _ ->
        {:error,
         [
           %ValidationError{
             path: path,
             mod: __MODULE__,
             msg: "cannot coerce into boolean",
             term: term
           }
         ]}
    end
  end

  @impl true
  def validate(%__MODULE__{}, path, term) do
    {:error,
     [%ValidationError{path: path, mod: __MODULE__, msg: "must be a boolean", term: term}]}
  end
end