lib/validators/none.ex

defmodule Dsv.None do
  use Dsv.Validator

  @moduledoc """

  `Dsv.None` validator provides a functions to check that there is no element on the provided `List` or `String` that meets any of the provided criteria.



  > #### Validate other types of data {: .tip}
  >
  > To validate arguments of other data types `FindAll` protocol must be implemented for this type.

  """

  message("Not all elements match all validators.")
  e_message("Not all elements match all validators.")

  @doc """
  Ensure that none of the validators pass the check for the given data.

  For a string, this validator iterates through all graphemes, checking if none of them pass validation for all provided validators. For a list, it iterates through all elements in the list, verifying that none of them pass the validation for all specified validators.

  Returns `:true` if all validators fail for all elements; otherwise, returns `:false.`

  ## Example
      iex> Dsv.None.valid?("abcd", format: ~r/^[a-z]$/, equal: "w")
      :true

      iex> Dsv.None.valid?("abcd", format: ~r/^[a-z]$/, equal: "a")
      :false

      iex> Dsv.None.valid?("abcd", format: ~r/^[a-d]$/, custom: &is_bitstring/1)
      :false

      iex> Dsv.None.valid?("abcd", format: ~r/^[a-c]$/, custom: &is_bitstring/1)
      :false

      iex> Dsv.None.valid?([1, 2, 3, "hello"], custom: &is_number/1, number: [gt: 2])
      :false

      iex> Dsv.None.valid?([1, 2, 3, "hello"], number: [gt: 0])
      :false

      iex> Dsv.None.valid?([1, 2, 3], custom: &is_number/1, number: [gte: 0, lte: 10])
      :false
  """
  def valid?(data, options),
    do:
      Iterable.iterate(data, :cont, fn elem ->
        if Dsv.valid?(elem, options), do: :halt, else: :cont
      end)
      |> (&(elem(&1, 1) == :empty)).()

  def valid?(data, options, binded_values),
    do:
      Iterable.iterate(data, :cont, fn elem ->
        if Dsv.valid?(elem, options, binded_values), do: :halt, else: :cont
      end)
      |> (&(elem(&1, 1) == :empty)).()

  @doc """
  Ensure that none of the validators pass the check for the given data and allow for custom error messages.

  For a string, this validator iterates through all graphemes, checking if none of them pass validation for all provided validators. For a list, it iterates through all elements in the list, verifying that none of them pass the validation for all specified validators.

  Returns `:ok` if all validators fail for all elements; otherwise, returns error ruple with the error message.


  ## Options
    * any validator name like
      * `:format`
      * `:custom`
      * `:number`
      * `:length`
      * `:equal`
      * `:data`
    * `:message` - (EEx string) message used in error tuple in case of validation failure.

  ## Example
      iex> Dsv.None.validate("abcd", format: ~r/^[a-z]$/, equal: "w")
      :ok

      iex> Dsv.None.validate("abcd", format: ~r/^[a-z]$/, equal: "a")
      {:error, "Not all elements match all validators."}

      iex> Dsv.None.validate("abcd", format: ~r/^[a-d]$/, custom: &is_bitstring/1)
      {:error, "Not all elements match all validators."}

      iex> Dsv.None.validate("abcd", format: ~r/^[a-c]$/, custom: &is_bitstring/1)
      {:error, "Not all elements match all validators."}

      iex> Dsv.None.validate([1, 2, 3, "hello"], custom: &is_number/1, number: [gt: 2])
      {:error, "Not all elements match all validators."}

      iex> Dsv.None.validate([1, 2, 3, "hello"], number: [gt: 0])
      {:error, "Not all elements match all validators."}

      iex> Dsv.None.validate([1, 2, 3], custom: &is_number/1, number: [gte: 0, lte: 10])
      {:error, "Not all elements match all validators."}

      iex> Dsv.None.validate([1, 2, 3, "hello"], number: [gt: 0], format: ~r/^[a-z]$/)
      {:error, "Not all elements match all validators."}

  ## Example with custom message
      iex> Dsv.None.validate("abcd", format: ~r/^[a-z]$/, equal: "w", message: "All elements must be small letter 'w'.")
      :ok

      iex> Dsv.None.validate("abcd", format: ~r/^[a-z]$/, equal: "a", message: "All elements must be small letter 'a'.")
      {:error, "All elements must be small letter 'a'."}

      iex> Dsv.None.validate("abcd", format: ~r/^[a-d]$/, custom: &is_bitstring/1, message: "All elements must be one of the letter: 'a', 'b', 'c', 'd'.")
      {:error, "All elements must be one of the letter: 'a', 'b', 'c', 'd'."}

      iex> Dsv.None.validate("abcd", format: ~r/^[a-c]$/, custom: &is_bitstring/1, message: "All elements must be one of the letter: 'a', 'b', 'c'.")
      {:error, "All elements must be one of the letter: 'a', 'b', 'c'."}

      iex> Dsv.None.validate([1, 2, 3, "hello"], custom: &is_number/1, number: [gt: 2], message: "All values must be a number greater that 2.")
      {:error, "All values must be a number greater that 2."}

      iex> Dsv.None.validate([1, 2, 3, "hello"], number: [gt: 0], message: "All values must be a number greater that 0.")
      {:error, "All values must be a number greater that 0."}

      iex> Dsv.None.validate([1, 2, 3], custom: &is_number/1, number: [gte: 0, lte: 10], message: "All values must be a number between 0 and 10.")
      {:error, "All values must be a number between 0 and 10."}

  """
  def validate(data, options), do: super(data, options)
end