defmodule Ash.Resource.Validation.Builtins do
@moduledoc """
Built in validations that are available to all resources
The functions in this module are imported by default in the validations section.
"""
alias Ash.Resource.Validation
@doc """
Validates that an attribute's value is in a given list
"""
def one_of(attribute, values) do
{Validation.OneOf, attribute: attribute, values: values}
end
@doc "Validates that an attribute is being changed"
def changing(field) do
{Validation.Changing, field: field}
end
@doc "Validates that a field or argument matches another field or argument"
def confirm(field, confirmation) do
{Validation.Confirm, [field: field, confirmation: confirmation]}
end
@doc "Validates that an attribute on the original record does not equal a specific value"
def attribute_does_not_equal(attribute, value) do
{Validation.AttributeDoesNotEqual, attribute: attribute, value: value}
end
@doc "Validates that an attribute on the original record equals a specific value"
def attribute_equals(attribute, value) do
{Validation.AttributeEquals, attribute: attribute, value: value}
end
@doc "Validates that an attribute on the original record meets the given length criteria"
def string_length(attribute, opts \\ []) do
{Validation.StringLength, Keyword.merge(opts, attribute: attribute)}
end
@doc "Validates that attribute meets the given criteria"
def compare(attribute, opts \\ []) do
{Validation.Compare, Keyword.merge(opts, attribute: attribute)}
end
@doc """
Validates that an attribute's value matches a given regex or string, using the provided error, message if not.
`String.match?/2` is used to determine if it matches.
"""
def match(attribute, match, message \\ nil) do
message = message || "must match #{match}"
{Validation.Match, attribute: attribute, match: match, message: message}
end
@doc """
Validates the presence of a list of attributes
If no options are provided, validates that they are all present.
#{Ash.OptionsHelpers.docs(Keyword.delete(Validation.Present.schema(), :attributes))}
"""
def present(attributes, opts \\ []) do
if opts == [] do
attributes = List.wrap(attributes)
{Validation.Present, attributes: attributes, exactly: Enum.count(attributes)}
else
opts = Keyword.put(opts, :attributes, List.wrap(attributes))
{Validation.Present, opts}
end
end
@doc """
Validates the absence of a list of attributes
If no options are provided, validates that they are all absent.
The docs behave the same as `present/2`, except they validate absence.
"""
def absent(attributes, opts \\ []) do
if opts == [] do
{Validation.Present, attributes: List.wrap(attributes), exactly: 0}
else
attributes = List.wrap(attributes)
count = Enum.count(attributes)
new_opts =
case Keyword.fetch(opts, :at_least) do
{:ok, value} ->
Keyword.put(opts, :at_most, count - value)
:error ->
Keyword.put(opts, :at_most, 0)
end
new_opts =
case Keyword.fetch(opts, :at_most) do
{:ok, value} ->
Keyword.put(new_opts, :at_least, count - value)
:error ->
Keyword.put(new_opts, :at_least, 0)
end
present(attributes, new_opts)
end
end
end