defmodule Dsv.Email do
use Dsv.Validator, complex: true
@moduledoc """
Dsv.Email module offers functions to validate an email address and optionally checks specific parts of it.
> #### Notes: {: .info}
> This function uses basic email format validation and does not guarantee
> that the email address is deliverable or exists.
"""
@email_regex ~r"\A(?<username>[\w!#$%&'*+/=?`{|}~^-]+(?:\.[\w!#$%&'*+/=?`{|}~^-]+)*)@(?<mail_server>[A-Z0-9-]+)\.+(?<top_level_domain>[A-Z]{2,6})\Z"i
@top_level_domain "top_level_domain"
@mail_server "mail_server"
@username "username"
message(&get_errors/3)
@doc """
The `valid?/2` function provides a robust mechanism to validate an email address
while applying a set of associated validators based on provided criteria.
## Parameters
* `email` (string) - The email address to be validated.
* `options` (keyword list) - A keyword list containing validation criteria for specific parts of the email.
Valid keys include:
- `:username` - Validators for the username part of the email (before "@").
- `:mail_server` - Validators for the mail server part of the email (between "@" and the last dot).
- `:top_level_domain` - Validators for the top-level domain part of the email (after the last dot).
## Returns
- `:true` Represents successful validation, where the email is correct and meets all the specified criteria.
- `:false` if the data (email) fails validation.
## Example
iex> Dsv.Email.valid?("mail.test@domain.com")
:true
iex> Dsv.Email.valid?("not-existing-email-address@domain.com")
:true
iex> Dsv.Email.valid?("this is not email address")
:false
iex> Dsv.Email.valid?("mail.test@domain.com", top_level_domain: [equal: "domain"], username: [format: ~r/m.*t.*/])
:false
iex> Dsv.Email.valid?("not-existing-email-address@domain.com", top_level_domain: [equal: "domain"], username: [format: ~r/m.*t.*/])
:false
iex> Dsv.Email.valid?("mail.test@domain.com", [])
:true
iex> Dsv.Email.valid?("this_is_not_real_email_address.com", top_level_domain: [equal: "com"])
:false
"""
def valid?(data, []), do: Regex.match?(@email_regex, data)
def valid?(data, options), do: valid?(data, []) and super(data, options)
def valid?(data, options, binded_values),
do: valid?(data, []) and super(data, options, binded_values)
@doc """
The `validate/2` function provides a robust mechanism to validate an email address
while applying a set of associated validators based on provided criteria.
## Parameters
* `email` (string) - The email address to be validated.
* `options` (keyword list) - A keyword list containing validation criteria for specific parts of the email.
Valid keys include:
- `:username` - Validators for the username part of the email (before "@").
- `:mail_server` - Validators for the mail server part of the email (between "@" and the last dot).
- `:top_level_domain` - Validators for the top-level domain part of the email (after the last dot).
- `:message` - Custom message returned in case of the validation failure.
## Returns
- `:ok` - Represents successful validation, where the email is correct and meets all the specified criteria.
- `{:error, errors}` - if the data (email) fails validation. The `errors` map provides detailed information about each part of the email that did not pass validation.
## Example
iex> Dsv.Email.validate("mail.test@domain.com")
:ok
iex> Dsv.Email.validate("not-existing-email-address@domain.com")
:ok
iex> Dsv.Email.validate("this is not email address")
{:error, "Invalid email address."}
iex> Dsv.Email.validate("mail.test@domain.com", top_level_domain: [equal: "domain"], username: [format: ~r/m.*t.*/])
{:error, %{top_level_domain: ["Values must be equal"]}}
iex> Dsv.Email.validate("not-existing-email-address@domain.com", top_level_domain: [equal: "domain"], username: [format: ~r/m.*t.*/])
{:error, %{top_level_domain: ["Values must be equal"], username: ["Value not-existing-email-address does not match pattern m.*t.*"]}}
iex> Dsv.Email.validate("mail.test@domain.com", [])
:ok
iex> Dsv.Email.validate("this_is_not_real_email_address.com", top_level_domain: [equal: "com"])
{:error, "Invalid email address."}
iex> Dsv.Email.validate("mail.test@domain.com", top_level_domain: [equal: "domain"], username: [format: ~r/m.*t.*/], message: "Email address don't meet validation criteria.")
{:error, "Email address don't meet validation criteria."}
"""
def validate(data, []),
do: if(valid?(data, []), do: :ok, else: {:error, "Invalid email address."})
def validate(data, options),
do: if(valid?(data, []), do: super(data, options), else: {:error, "Invalid email address."})
defp get_element(data, :username),
do: Regex.named_captures(@email_regex, data) |> Map.get(@username)
defp get_element(data, :mail_server),
do: Regex.named_captures(@email_regex, data) |> Map.get(@mail_server)
defp get_element(data, :top_level_domain),
do: Regex.named_captures(@email_regex, data) |> Map.get(@top_level_domain)
end