defmodule Mv.Membership.Email do @moduledoc """ Custom Ash type for validated email addresses. ## Overview This type extends `:string` with email-specific validation constraints. It ensures that email values stored in CustomFieldValue resources are valid email addresses according to a standard regex pattern. ## Validation Rules - Minimum length: 5 characters - Maximum length: 254 characters (RFC 5321 maximum) - Pattern: Standard email format (username@domain.tld) - Automatic trimming of leading/trailing whitespace ## Usage This type is used in the CustomFieldValue union type for custom fields with `value_type: :email` in CustomField definitions. ## Example # In a custom field definition CustomField.create!(%{ name: "work_email", value_type: :email }) # Valid values "user@example.com" "first.last@company.co.uk" # Invalid values "not-an-email" # Missing @ and domain "a@b" # Too short """ @match_pattern ~S/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/ @match_regex Regex.compile!(@match_pattern) @min_length 5 @max_length 254 use Ash.Type.NewType, subtype_of: :string, constraints: [ match: @match_pattern, trim?: true, min_length: @min_length, max_length: @max_length ] @impl true def cast_input(value, _) when is_binary(value) do value = String.trim(value) cond do String.length(value) < @min_length -> :error String.length(value) > @max_length -> :error !Regex.match?(@match_regex, value) -> :error true -> {:ok, value} end end @impl true def cast_input(_, _), do: :error end