# `Text.Extract.Email`
[🔗](https://github.com/kipcole9/text/blob/v0.6.1/lib/extract/email.ex#L1)

Phase 2 validator for email-address candidates.

Takes a scanner candidate span and validates it as an email address
per RFC 5322 §3.4.1 (dot-atom local part) and RFC 6531 (SMTPUTF8 /
EAI — internationalised local parts) when the `:eai` option is on.

The host part is validated identically to URL hosts: per-label IDNA
via `Unicode.IDNA.to_ascii/2`, then TLD lookup against the bundled
IANA list.

Returns a structured `%{}` record on success or `{:error, reason}` on
rejection.

# `email_record`

```elixir
@type email_record() :: %{
  email: String.t(),
  ascii: String.t(),
  span: {non_neg_integer(), non_neg_integer()},
  local: String.t(),
  host: String.t(),
  ascii_host: String.t()
}
```

Parsed email record.

# `reason`

```elixir
@type reason() ::
  :empty
  | :no_local
  | :no_host
  | :invalid_local
  | :invalid_host
  | :idna_failed
  | :invalid_tld
```

Reasons for rejecting an email candidate.

# `validate`

```elixir
@spec validate(String.t(), {non_neg_integer(), non_neg_integer()}, keyword()) ::
  {:ok, email_record()} | {:error, reason()}
```

Validates an email candidate span.

### Arguments

* `candidate` is the candidate substring as emitted by
  `Text.Extract.Scanner.scan/1`.

* `span` is the `{start_byte, length_bytes}` tuple positioning
  `candidate` within the original source text — preserved through
  to the returned record's `:span` field.

### Options

* `:eai` — when `true`, allow non-ASCII codepoints in the local part
  per RFC 6531 SMTPUTF8. Default `true`.

* `:tld_mode` — `:iana` (default) or `:any`.

* `:strict_idn` — when `true`, IDNA uses STD3 ASCII rules. Default
  `false`.

### Returns

* `{:ok, record}` on success.

* `{:error, reason}` on rejection.

### Examples

    iex> {:ok, r} = Text.Extract.Email.validate("alice@example.com", {0, 17})
    iex> {r.local, r.host, r.span}
    {"alice", "example.com", {0, 17}}

    iex> Text.Extract.Email.validate("alice@example.fake", {0, 18})
    {:error, :invalid_tld}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
