defmodule Dsv.Any do
use Dsv.Validator
@moduledoc """
Ensure that at least one element passes the check for all validators.
In the case of a string, this validator will iterate through all graphemes and check if at least one passes the validation for all validator.
In the case of a list, this validator will iterate through all elements and check if at least one passes the validation for all validators.
> #### Validate other types of data {: .tip}
>
> To validate arguments of other data types `FindAany` protocol must be implemented for this type.
"""
message("There is no element matching all validators.")
@doc """
Ensure that at least one element passes the check for all validators.
In the case of a string, this validator will iterate through all graphemes and check if at least one passes the validation for all validator.
In the case of a list, this validator will iterate through all elements and check if at least one passes the validation for all validators.
Returns `:true` if all validators pass for at least one element; otherwise, returns `:false`.
## Example
iex> Dsv.Any.valid?("abcd", format: ~r/^[a-z]$/, equal: "w")
:false
iex> Dsv.Any.valid?("abcd", format: ~r/^[a-z]$/, equal: "a")
:true
iex> Dsv.Any.valid?("abcd", format: ~r/^[k-z]$/, equal: "a")
:false
iex> Dsv.Any.valid?("abcd", format: ~r/^[a-d]$/, custom: &is_bitstring/1)
:true
iex> Dsv.Any.valid?("abcd", format: ~r/^[g-j]$/, custom: &is_bitstring/1)
:false
iex> Dsv.Any.valid?([1, 2, "test", 3], custom: &is_number/1, number: [gte: 3, lte: 5])
:true
iex> Dsv.Any.valid?([1, {:a, 1}, "test", 3], custom: &is_tuple/1, custom: &({:a, 1} == &1))
:true
iex> Dsv.Any.valid?([1, {:a, 2}, "test", 3], custom: &is_tuple/1, custom: &({:a, 1} == &1))
:false
iex> Dsv.Any.valid?([1, "{:a, 1}", "test", 3], custom: &is_tuple/1, custom: &({:a, 1} == &1))
:false
iex> Dsv.Any.valid?({1, 2, 3}, equal: 3)
:true
"""
def valid?(data, options),
do:
Iterable.iterate(data, :cont, fn elem ->
if Dsv.valid?(elem, options), do: :halt, else: :cont
end)
|> (&(elem(&1, 1) == :not_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) == :not_empty)).()
@doc """
Ensure that at least one element passes the check for all validators.
In the case of a string, this validator will iterate through all graphemes and check if at least one passes the validation for all validator.
In the case of a list, this validator will iterate through all elements and check if at least one passes the validation for all validators.
Returns `:ok` if all validators pass for at least one element; otherwise, returns error tuple with the error message.
## Example
iex> Dsv.Any.validate("abcd", format: ~r/^[a-z]$/, equal: "w")
{:error, "There is no element matching all validators."}
iex> Dsv.Any.validate("abcd", format: ~r/^[a-z]$/, equal: "a")
:ok
iex> Dsv.Any.validate("abcd", format: ~r/^[k-z]$/, equal: "a")
{:error, "There is no element matching all validators."}
iex> Dsv.Any.validate("abcd", format: ~r/^[a-d]$/, custom: &is_bitstring/1)
:ok
iex> Dsv.Any.validate("abcd", format: ~r/^[g-j]$/, custom: &is_bitstring/1)
{:error, "There is no element matching all validators."}
iex> Dsv.Any.validate([1, 2, "test", 3], custom: &is_number/1, number: [gte: 3, lte: 5])
:ok
iex> Dsv.Any.validate([1, {:a, 1}, "test", 3], custom: &is_tuple/1, custom: &({:a, 1} == &1))
:ok
iex> Dsv.Any.validate([1, {:a, 2}, "test", 3], custom: &is_tuple/1, custom: &({:a, 1} == &1))
{:error, "There is no element matching all validators."}
iex> Dsv.Any.validate([1, "{:a, 1}", "test", 3], custom: &is_tuple/1, custom: &({:a, 1} == &1))
{:error, "There is no element matching all validators."}
To return custom message, use :message key in the last argument.
## Example
iex> Dsv.Any.validate("abcd", format: ~r/^[a-z]$/, equal: "w", message: "At least one letter must be 'w'.")
{:error, "At least one letter must be 'w'."}
iex> Dsv.Any.validate("abcd", format: ~r/^[a-z]$/, equal: "a", message: "At least one letter must be 'a'.")
:ok
iex> Dsv.Any.validate("abcd", format: ~r/^[k-z]$/, equal: "a", message: "At least one letter must be 'a' and any letter between k and z at the same moment. Sorry.")
{:error, "At least one letter must be 'a' and any letter between k and z at the same moment. Sorry."}
iex> Dsv.Any.validate("abcd", format: ~r/^[a-d]$/, custom: &is_bitstring/1, message: "At least one letter must be 'a', 'b', 'c' or 'd'.")
:ok
iex> Dsv.Any.validate("abcd", format: ~r/^[g-j]$/, custom: &is_bitstring/1, message: "At least one letter must be 'g', 'h', 'i' or 'j'.")
{:error, "At least one letter must be 'g', 'h', 'i' or 'j'."}
iex> Dsv.Any.validate([1, 2, "test", 3], custom: &is_number/1, number: [gte: 3, lte: 5], message: "At least one number must be between 3 and 5.")
:ok
iex> Dsv.Any.validate([1, {:a, 1}, "test", 3], custom: &is_tuple/1, custom: &({:a, 1} == &1), message: "At least on element must be a tuple `{:a, 1}`.")
:ok
iex> Dsv.Any.validate([1, {:a, 2}, "test", 3], custom: &is_tuple/1, custom: &({:a, 1} == &1), message: "At least on element must be a tuple `{:a, 1}`.")
{:error, "At least on element must be a tuple `{:a, 1}`."}
iex> Dsv.Any.validate([1, "{:a, 1}", "test", 3], custom: &is_tuple/1, custom: &({:a, 1} == &1), message: "At least on element from the list `<%= inspect data %>` must be a tuple `{:a, 1}`.")
{:error, ~s(At least on element from the list `[1, "{:a, 1}", "test", 3]` must be a tuple `{:a, 1}`.)}
"""
def validate(data, options), do: super(data, options)
end